pidlens 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pidlens-0.1.0/LICENSE +21 -0
- pidlens-0.1.0/PKG-INFO +296 -0
- pidlens-0.1.0/README.md +260 -0
- pidlens-0.1.0/pyproject.toml +55 -0
- pidlens-0.1.0/setup.cfg +4 -0
- pidlens-0.1.0/src/pidlens/__init__.py +5 -0
- pidlens-0.1.0/src/pidlens/__main__.py +8 -0
- pidlens-0.1.0/src/pidlens/cli.py +111 -0
- pidlens-0.1.0/src/pidlens/collect.py +217 -0
- pidlens-0.1.0/src/pidlens/humanize.py +127 -0
- pidlens-0.1.0/src/pidlens/layout.py +196 -0
- pidlens-0.1.0/src/pidlens/parse.py +206 -0
- pidlens-0.1.0/src/pidlens/report.py +286 -0
- pidlens-0.1.0/src/pidlens/system.py +36 -0
- pidlens-0.1.0/src/pidlens.egg-info/PKG-INFO +296 -0
- pidlens-0.1.0/src/pidlens.egg-info/SOURCES.txt +24 -0
- pidlens-0.1.0/src/pidlens.egg-info/dependency_links.txt +1 -0
- pidlens-0.1.0/src/pidlens.egg-info/entry_points.txt +2 -0
- pidlens-0.1.0/src/pidlens.egg-info/requires.txt +5 -0
- pidlens-0.1.0/src/pidlens.egg-info/top_level.txt +1 -0
- pidlens-0.1.0/tests/test_cli.py +83 -0
- pidlens-0.1.0/tests/test_collect.py +138 -0
- pidlens-0.1.0/tests/test_humanize.py +85 -0
- pidlens-0.1.0/tests/test_layout.py +153 -0
- pidlens-0.1.0/tests/test_parse.py +178 -0
- pidlens-0.1.0/tests/test_report.py +108 -0
pidlens-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sayed Tauseef Naqvi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pidlens-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pidlens
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Everything macOS knows about a running process, in one readable report.
|
|
5
|
+
Author-email: Sayed Tauseef Naqvi <tauseef.naqvi786@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/sthnaqvi/pidlens
|
|
8
|
+
Project-URL: Repository, https://github.com/sthnaqvi/pidlens
|
|
9
|
+
Project-URL: Issues, https://github.com/sthnaqvi/pidlens/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/sthnaqvi/pidlens/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: macos,process,ps,lsof,pid,cli,sysadmin,debugging,forensics,codesign,launchd
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: System Administrators
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Topic :: System :: Monitoring
|
|
26
|
+
Classifier: Topic :: System :: Systems Administration
|
|
27
|
+
Classifier: Topic :: Utilities
|
|
28
|
+
Requires-Python: >=3.8
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
33
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
34
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# pidlens
|
|
38
|
+
|
|
39
|
+
**Everything macOS knows about a running process, in one readable report.**
|
|
40
|
+
|
|
41
|
+
[](https://github.com/sthnaqvi/pidlens/actions/workflows/ci.yml)
|
|
42
|
+
[](https://pypi.org/project/pidlens/)
|
|
43
|
+
[](https://www.npmjs.com/package/pidlens)
|
|
44
|
+
[](LICENSE)
|
|
45
|
+
|
|
46
|
+
`ps` tells you a process exists. `lsof` tells you what it has open. `codesign`
|
|
47
|
+
tells you who signed it. `launchctl` tells you who keeps it alive. **pidlens
|
|
48
|
+
asks all of them and prints one report you can actually read** — decoded into
|
|
49
|
+
English, wrapped to your terminal, no flags to memorise.
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
pidlens 28958
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
╭──────────────────────────────────────────────────────────────────────────╮
|
|
57
|
+
│ Finder (pid 594) │
|
|
58
|
+
╰──────────────────────────────────────────────────────────────────────────╯
|
|
59
|
+
── IDENTITY ────────────────────────────────────────────────────────────────
|
|
60
|
+
Name : Finder
|
|
61
|
+
PID : 594
|
|
62
|
+
Parent PID : 1
|
|
63
|
+
Owner : alex (uid 501)
|
|
64
|
+
Terminal : none — not attached to a terminal
|
|
65
|
+
State : sleeping (waiting, idle under 20s)
|
|
66
|
+
Scheduling : priority 46, nice 0 (normal)
|
|
67
|
+
Process group : 594
|
|
68
|
+
Session : 0
|
|
69
|
+
|
|
70
|
+
── WHEN IT STARTED ─────────────────────────────────────────────────────────
|
|
71
|
+
Started at : Wednesday, 5 August 2026 at 9:38:15 PM
|
|
72
|
+
Running for : 22 days, 21 hours, 22 min
|
|
73
|
+
CPU time used : 36 min, 53 sec (0.1% of one core over its lifetime)
|
|
74
|
+
Booted at : Wednesday, 5 August 2026 at 9:37:59 PM
|
|
75
|
+
Vs. boot : started 16 sec after boot → a startup service, not
|
|
76
|
+
something you launched
|
|
77
|
+
Unix epoch : 1785946095
|
|
78
|
+
|
|
79
|
+
── WHAT IS RUNNING ─────────────────────────────────────────────────────────
|
|
80
|
+
Executable : /System/Library/CoreServices/Finder.app/Contents/MacOS
|
|
81
|
+
/Finder
|
|
82
|
+
Arguments : none
|
|
83
|
+
Working dir : /
|
|
84
|
+
── WHO STARTED IT ──────────────────────────────────────────────────────────
|
|
85
|
+
[1] root
|
|
86
|
+
/sbin/launchd
|
|
87
|
+
└─ [594] alex
|
|
88
|
+
/System/Library/CoreServices/Finder.app/Contents/MacOS/Finder
|
|
89
|
+
|
|
90
|
+
── RESOURCE USAGE ──────────────────────────────────────────────────────────
|
|
91
|
+
CPU now : 0.0%
|
|
92
|
+
Memory : 58.4 MB resident (0.3% of RAM)
|
|
93
|
+
Virtual size : 398.8 GB
|
|
94
|
+
virtual size is address space reserved, not memory in use
|
|
95
|
+
Threads : 9
|
|
96
|
+
Open handles : 244
|
|
97
|
+
|
|
98
|
+
── NETWORK ─────────────────────────────────────────────────────────────────
|
|
99
|
+
no network sockets open
|
|
100
|
+
plus 1 unix-domain socket(s) for local IPC
|
|
101
|
+
|
|
102
|
+
── THE BINARY ON DISK ──────────────────────────────────────────────────────
|
|
103
|
+
Path : /System/Library/CoreServices/Finder.app/Contents/MacOS
|
|
104
|
+
/Finder
|
|
105
|
+
Size : 23.6 MB
|
|
106
|
+
Modified : 17 August 2025 at 12:14 AM
|
|
107
|
+
Owner : root, mode 755
|
|
108
|
+
Architectures : arm64e, x86_64
|
|
109
|
+
Signed by : Software Signing
|
|
110
|
+
Chain : Apple Code Signing Certification Authority → Apple Root CA
|
|
111
|
+
Bundle ID : com.apple.finder
|
|
112
|
+
Team ID : —
|
|
113
|
+
Signed on : 12 Jul 2025 at 2:00:36 PM
|
|
114
|
+
Entitlements : 88 granted
|
|
115
|
+
· com.apple.accounts.appleaccount.fullaccess
|
|
116
|
+
· com.apple.amp.devices.client
|
|
117
|
+
· com.apple.application-identifier
|
|
118
|
+
── SERVICE / LAUNCHD ───────────────────────────────────────────────────────
|
|
119
|
+
Managed job : com.apple.Finder
|
|
120
|
+
Last exit : 0
|
|
121
|
+
launchd owns this — it is restarted automatically when it exits
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
…
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Install
|
|
128
|
+
|
|
129
|
+
**Homebrew**
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
brew tap sthnaqvi/tap
|
|
133
|
+
brew install pidlens
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**npm**
|
|
137
|
+
|
|
138
|
+
```sh
|
|
139
|
+
npm install -g pidlens
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**pip** (or [pipx](https://pipx.pypa.io), which keeps it out of your system Python)
|
|
143
|
+
|
|
144
|
+
```sh
|
|
145
|
+
pipx install pidlens # recommended
|
|
146
|
+
pip install pidlens
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**From source**
|
|
150
|
+
|
|
151
|
+
```sh
|
|
152
|
+
git clone https://github.com/sthnaqvi/pidlens.git
|
|
153
|
+
cd pidlens && make venv && make test && pip install .
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The npm package bundles the same Python program and only needs Python 3.8+ on
|
|
157
|
+
your PATH — nothing is compiled or downloaded at install time. Set
|
|
158
|
+
`PIDLENS_PYTHON` to pick a specific interpreter.
|
|
159
|
+
|
|
160
|
+
## Usage
|
|
161
|
+
|
|
162
|
+
```sh
|
|
163
|
+
pidlens 28958 # by pid
|
|
164
|
+
pidlens Finder # by name — exact first, then loose, then argv
|
|
165
|
+
pidlens node --all # don't truncate long lists
|
|
166
|
+
pidlens 28958 --json | jq . # same data, machine-readable
|
|
167
|
+
pidlens 28958 -s timing,binary # only the sections you want
|
|
168
|
+
sudo pidlens 28958 # also reveals environment variables
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
| Flag | What it does |
|
|
172
|
+
| --- | --- |
|
|
173
|
+
| `-a`, `--all` | Print every open file, environment variable and entitlement instead of the first few |
|
|
174
|
+
| `-j`, `--json` | Emit the collected data as JSON — the same values the report renders from |
|
|
175
|
+
| `-s`, `--sections LIST` | Render only these sections, comma separated |
|
|
176
|
+
| `-w`, `--width N` | Wrap to N columns instead of detecting the terminal |
|
|
177
|
+
| `--no-color` | Disable ANSI colour (also honours `NO_COLOR` and non-tty output) |
|
|
178
|
+
| `-V`, `--version` | Print the version |
|
|
179
|
+
|
|
180
|
+
Exit codes: `0` report produced · `2` no target given · `3` no such process ·
|
|
181
|
+
`4` not macOS.
|
|
182
|
+
|
|
183
|
+
## What each section answers
|
|
184
|
+
|
|
185
|
+
| Section | The question it answers |
|
|
186
|
+
| --- | --- |
|
|
187
|
+
| `identity` | Who owns it, what state it's in, and what that state actually means |
|
|
188
|
+
| `timing` | **When did it start**, how long has it run, how much CPU has it burned, and did it start at boot (so: a system service, not something you launched) |
|
|
189
|
+
| `command` | Which binary, which arguments, from which working directory |
|
|
190
|
+
| `environment` | Its environment variables, with credential-shaped values masked |
|
|
191
|
+
| `ancestry` | The chain of parents up to `launchd`, plus its children |
|
|
192
|
+
| `resources` | CPU, resident memory, virtual size, threads, open handles |
|
|
193
|
+
| `network` | Every socket in English — listening ports, established peers, unbound sockets |
|
|
194
|
+
| `files` | Open files grouped into libraries, real files, pipes and devices |
|
|
195
|
+
| `binary` | Size, architectures, signing authority and chain, Team ID, signing date, entitlements, Gatekeeper verdict, quarantine flag, installing package |
|
|
196
|
+
| `launchd` | Whether launchd owns it and will restart it |
|
|
197
|
+
| `commands` | Copy-paste `fs_usage`, `nettop`, `log stream`, `sample`, `spindump`, `lldb` lines with the pid already filled in |
|
|
198
|
+
|
|
199
|
+
## Answering "when did this start?"
|
|
200
|
+
|
|
201
|
+
The `timing` section is the one most people come for:
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
Started at : Wednesday, 5 August 2026 at 9:38:15 PM
|
|
205
|
+
Running for : 22 days, 21 hours, 22 min
|
|
206
|
+
CPU time used : 36 min, 53 sec (0.1% of one core over its lifetime)
|
|
207
|
+
Booted at : Wednesday, 5 August 2026 at 9:37:59 PM
|
|
208
|
+
Vs. boot : started 16 sec after boot → a startup service, not
|
|
209
|
+
something you launched
|
|
210
|
+
Unix epoch : 1785946095
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`ps -o lstart` gives you the first line. The value is in the rest: elapsed time
|
|
214
|
+
in units you can hold in your head, CPU time as a share of one core, and the
|
|
215
|
+
comparison against boot that tells you whether anyone ever chose to run this.
|
|
216
|
+
|
|
217
|
+
## JSON
|
|
218
|
+
|
|
219
|
+
Every value in the report comes from one dict, and `--json` prints it:
|
|
220
|
+
|
|
221
|
+
```sh
|
|
222
|
+
pidlens Finder --json | jq '.timing.started_human, .binary.signature.authority'
|
|
223
|
+
pidlens node --json | jq '.sockets[].description'
|
|
224
|
+
pidlens 28958 --json | jq '.environment.variables'
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## How it works
|
|
228
|
+
|
|
229
|
+
pidlens is **read-only**. It never attaches to, signals, or modifies the process
|
|
230
|
+
it describes — it shells out to tools already on your Mac and parses what they
|
|
231
|
+
print:
|
|
232
|
+
|
|
233
|
+
| Tool | Used for |
|
|
234
|
+
| --- | --- |
|
|
235
|
+
| `ps` | identity, state, priority, start time, elapsed and CPU time, argv, environment |
|
|
236
|
+
| `lsof` | open files, sockets, working directory, handle count |
|
|
237
|
+
| `pgrep` | name matching and children |
|
|
238
|
+
| `sysctl` | boot time, to place the start time in context |
|
|
239
|
+
| `codesign` | signing authority, Team ID, signing date, entitlements |
|
|
240
|
+
| `spctl` | the Gatekeeper verdict (assessed on the app bundle, not the inner executable) |
|
|
241
|
+
| `xattr`, `pkgutil`, `file` | quarantine flag, installing package, architectures |
|
|
242
|
+
| `launchctl` | whether it's a managed job |
|
|
243
|
+
|
|
244
|
+
Two consequences worth knowing:
|
|
245
|
+
|
|
246
|
+
- **Environment variables need root.** macOS only exposes another process's
|
|
247
|
+
environment to root, even your own processes. `sudo pidlens PID` shows them.
|
|
248
|
+
Names that look like credentials (`*_TOKEN`, `*_SECRET`, `*PASSWORD*`, …) are
|
|
249
|
+
masked before printing either way.
|
|
250
|
+
- **Gatekeeper "not assessed" is normal.** `spctl` only rates app bundles, so a
|
|
251
|
+
plain executable comes back unassessable even when its signature is perfectly
|
|
252
|
+
valid. pidlens says that, instead of reporting a scary "rejected".
|
|
253
|
+
|
|
254
|
+
## Requirements
|
|
255
|
+
|
|
256
|
+
macOS (tested on Sonoma and Sequoia) and Python 3.8 or newer. No third-party
|
|
257
|
+
dependencies — the whole tool is the standard library plus the system tools
|
|
258
|
+
above.
|
|
259
|
+
|
|
260
|
+
## Development
|
|
261
|
+
|
|
262
|
+
```sh
|
|
263
|
+
make venv # local .venv with dev dependencies
|
|
264
|
+
make test # 550+ tests, no live processes required for most of them
|
|
265
|
+
make demo # run pidlens against your shell
|
|
266
|
+
make build # sdist + wheel
|
|
267
|
+
make npm-pack # build the npm tarball
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
The code is split so that almost all of it is testable without a running
|
|
271
|
+
process:
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
src/pidlens/
|
|
275
|
+
humanize.py bytes, durations, ps state codes → English (pure)
|
|
276
|
+
layout.py width-aware wrapping and the Renderer (pure)
|
|
277
|
+
parse.py one parser per tool's output format (pure)
|
|
278
|
+
system.py the only module that shells out
|
|
279
|
+
collect.py builds the data model for one pid
|
|
280
|
+
report.py turns the data model into sections
|
|
281
|
+
cli.py arguments, exit codes, JSON
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Tests use fixtures captured from real `lsof`, `codesign`, `spctl` and
|
|
285
|
+
`launchctl` output, plus a `FakeRunner` that answers commands from a dict — so
|
|
286
|
+
the parsers and the entire report are verified without touching a live process.
|
|
287
|
+
The layout suite asserts that **no line ever exceeds the terminal width** and
|
|
288
|
+
that wrapping loses no characters, across widths from 38 to 110 columns.
|
|
289
|
+
|
|
290
|
+
## Contributing
|
|
291
|
+
|
|
292
|
+
Issues and pull requests are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
293
|
+
|
|
294
|
+
## License
|
|
295
|
+
|
|
296
|
+
MIT © Sayed Tauseef Naqvi
|
pidlens-0.1.0/README.md
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# pidlens
|
|
2
|
+
|
|
3
|
+
**Everything macOS knows about a running process, in one readable report.**
|
|
4
|
+
|
|
5
|
+
[](https://github.com/sthnaqvi/pidlens/actions/workflows/ci.yml)
|
|
6
|
+
[](https://pypi.org/project/pidlens/)
|
|
7
|
+
[](https://www.npmjs.com/package/pidlens)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
`ps` tells you a process exists. `lsof` tells you what it has open. `codesign`
|
|
11
|
+
tells you who signed it. `launchctl` tells you who keeps it alive. **pidlens
|
|
12
|
+
asks all of them and prints one report you can actually read** — decoded into
|
|
13
|
+
English, wrapped to your terminal, no flags to memorise.
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
pidlens 28958
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
╭──────────────────────────────────────────────────────────────────────────╮
|
|
21
|
+
│ Finder (pid 594) │
|
|
22
|
+
╰──────────────────────────────────────────────────────────────────────────╯
|
|
23
|
+
── IDENTITY ────────────────────────────────────────────────────────────────
|
|
24
|
+
Name : Finder
|
|
25
|
+
PID : 594
|
|
26
|
+
Parent PID : 1
|
|
27
|
+
Owner : alex (uid 501)
|
|
28
|
+
Terminal : none — not attached to a terminal
|
|
29
|
+
State : sleeping (waiting, idle under 20s)
|
|
30
|
+
Scheduling : priority 46, nice 0 (normal)
|
|
31
|
+
Process group : 594
|
|
32
|
+
Session : 0
|
|
33
|
+
|
|
34
|
+
── WHEN IT STARTED ─────────────────────────────────────────────────────────
|
|
35
|
+
Started at : Wednesday, 5 August 2026 at 9:38:15 PM
|
|
36
|
+
Running for : 22 days, 21 hours, 22 min
|
|
37
|
+
CPU time used : 36 min, 53 sec (0.1% of one core over its lifetime)
|
|
38
|
+
Booted at : Wednesday, 5 August 2026 at 9:37:59 PM
|
|
39
|
+
Vs. boot : started 16 sec after boot → a startup service, not
|
|
40
|
+
something you launched
|
|
41
|
+
Unix epoch : 1785946095
|
|
42
|
+
|
|
43
|
+
── WHAT IS RUNNING ─────────────────────────────────────────────────────────
|
|
44
|
+
Executable : /System/Library/CoreServices/Finder.app/Contents/MacOS
|
|
45
|
+
/Finder
|
|
46
|
+
Arguments : none
|
|
47
|
+
Working dir : /
|
|
48
|
+
── WHO STARTED IT ──────────────────────────────────────────────────────────
|
|
49
|
+
[1] root
|
|
50
|
+
/sbin/launchd
|
|
51
|
+
└─ [594] alex
|
|
52
|
+
/System/Library/CoreServices/Finder.app/Contents/MacOS/Finder
|
|
53
|
+
|
|
54
|
+
── RESOURCE USAGE ──────────────────────────────────────────────────────────
|
|
55
|
+
CPU now : 0.0%
|
|
56
|
+
Memory : 58.4 MB resident (0.3% of RAM)
|
|
57
|
+
Virtual size : 398.8 GB
|
|
58
|
+
virtual size is address space reserved, not memory in use
|
|
59
|
+
Threads : 9
|
|
60
|
+
Open handles : 244
|
|
61
|
+
|
|
62
|
+
── NETWORK ─────────────────────────────────────────────────────────────────
|
|
63
|
+
no network sockets open
|
|
64
|
+
plus 1 unix-domain socket(s) for local IPC
|
|
65
|
+
|
|
66
|
+
── THE BINARY ON DISK ──────────────────────────────────────────────────────
|
|
67
|
+
Path : /System/Library/CoreServices/Finder.app/Contents/MacOS
|
|
68
|
+
/Finder
|
|
69
|
+
Size : 23.6 MB
|
|
70
|
+
Modified : 17 August 2025 at 12:14 AM
|
|
71
|
+
Owner : root, mode 755
|
|
72
|
+
Architectures : arm64e, x86_64
|
|
73
|
+
Signed by : Software Signing
|
|
74
|
+
Chain : Apple Code Signing Certification Authority → Apple Root CA
|
|
75
|
+
Bundle ID : com.apple.finder
|
|
76
|
+
Team ID : —
|
|
77
|
+
Signed on : 12 Jul 2025 at 2:00:36 PM
|
|
78
|
+
Entitlements : 88 granted
|
|
79
|
+
· com.apple.accounts.appleaccount.fullaccess
|
|
80
|
+
· com.apple.amp.devices.client
|
|
81
|
+
· com.apple.application-identifier
|
|
82
|
+
── SERVICE / LAUNCHD ───────────────────────────────────────────────────────
|
|
83
|
+
Managed job : com.apple.Finder
|
|
84
|
+
Last exit : 0
|
|
85
|
+
launchd owns this — it is restarted automatically when it exits
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
…
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Install
|
|
92
|
+
|
|
93
|
+
**Homebrew**
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
brew tap sthnaqvi/tap
|
|
97
|
+
brew install pidlens
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**npm**
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
npm install -g pidlens
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**pip** (or [pipx](https://pipx.pypa.io), which keeps it out of your system Python)
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
pipx install pidlens # recommended
|
|
110
|
+
pip install pidlens
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**From source**
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
git clone https://github.com/sthnaqvi/pidlens.git
|
|
117
|
+
cd pidlens && make venv && make test && pip install .
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The npm package bundles the same Python program and only needs Python 3.8+ on
|
|
121
|
+
your PATH — nothing is compiled or downloaded at install time. Set
|
|
122
|
+
`PIDLENS_PYTHON` to pick a specific interpreter.
|
|
123
|
+
|
|
124
|
+
## Usage
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
pidlens 28958 # by pid
|
|
128
|
+
pidlens Finder # by name — exact first, then loose, then argv
|
|
129
|
+
pidlens node --all # don't truncate long lists
|
|
130
|
+
pidlens 28958 --json | jq . # same data, machine-readable
|
|
131
|
+
pidlens 28958 -s timing,binary # only the sections you want
|
|
132
|
+
sudo pidlens 28958 # also reveals environment variables
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
| Flag | What it does |
|
|
136
|
+
| --- | --- |
|
|
137
|
+
| `-a`, `--all` | Print every open file, environment variable and entitlement instead of the first few |
|
|
138
|
+
| `-j`, `--json` | Emit the collected data as JSON — the same values the report renders from |
|
|
139
|
+
| `-s`, `--sections LIST` | Render only these sections, comma separated |
|
|
140
|
+
| `-w`, `--width N` | Wrap to N columns instead of detecting the terminal |
|
|
141
|
+
| `--no-color` | Disable ANSI colour (also honours `NO_COLOR` and non-tty output) |
|
|
142
|
+
| `-V`, `--version` | Print the version |
|
|
143
|
+
|
|
144
|
+
Exit codes: `0` report produced · `2` no target given · `3` no such process ·
|
|
145
|
+
`4` not macOS.
|
|
146
|
+
|
|
147
|
+
## What each section answers
|
|
148
|
+
|
|
149
|
+
| Section | The question it answers |
|
|
150
|
+
| --- | --- |
|
|
151
|
+
| `identity` | Who owns it, what state it's in, and what that state actually means |
|
|
152
|
+
| `timing` | **When did it start**, how long has it run, how much CPU has it burned, and did it start at boot (so: a system service, not something you launched) |
|
|
153
|
+
| `command` | Which binary, which arguments, from which working directory |
|
|
154
|
+
| `environment` | Its environment variables, with credential-shaped values masked |
|
|
155
|
+
| `ancestry` | The chain of parents up to `launchd`, plus its children |
|
|
156
|
+
| `resources` | CPU, resident memory, virtual size, threads, open handles |
|
|
157
|
+
| `network` | Every socket in English — listening ports, established peers, unbound sockets |
|
|
158
|
+
| `files` | Open files grouped into libraries, real files, pipes and devices |
|
|
159
|
+
| `binary` | Size, architectures, signing authority and chain, Team ID, signing date, entitlements, Gatekeeper verdict, quarantine flag, installing package |
|
|
160
|
+
| `launchd` | Whether launchd owns it and will restart it |
|
|
161
|
+
| `commands` | Copy-paste `fs_usage`, `nettop`, `log stream`, `sample`, `spindump`, `lldb` lines with the pid already filled in |
|
|
162
|
+
|
|
163
|
+
## Answering "when did this start?"
|
|
164
|
+
|
|
165
|
+
The `timing` section is the one most people come for:
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
Started at : Wednesday, 5 August 2026 at 9:38:15 PM
|
|
169
|
+
Running for : 22 days, 21 hours, 22 min
|
|
170
|
+
CPU time used : 36 min, 53 sec (0.1% of one core over its lifetime)
|
|
171
|
+
Booted at : Wednesday, 5 August 2026 at 9:37:59 PM
|
|
172
|
+
Vs. boot : started 16 sec after boot → a startup service, not
|
|
173
|
+
something you launched
|
|
174
|
+
Unix epoch : 1785946095
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
`ps -o lstart` gives you the first line. The value is in the rest: elapsed time
|
|
178
|
+
in units you can hold in your head, CPU time as a share of one core, and the
|
|
179
|
+
comparison against boot that tells you whether anyone ever chose to run this.
|
|
180
|
+
|
|
181
|
+
## JSON
|
|
182
|
+
|
|
183
|
+
Every value in the report comes from one dict, and `--json` prints it:
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
pidlens Finder --json | jq '.timing.started_human, .binary.signature.authority'
|
|
187
|
+
pidlens node --json | jq '.sockets[].description'
|
|
188
|
+
pidlens 28958 --json | jq '.environment.variables'
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## How it works
|
|
192
|
+
|
|
193
|
+
pidlens is **read-only**. It never attaches to, signals, or modifies the process
|
|
194
|
+
it describes — it shells out to tools already on your Mac and parses what they
|
|
195
|
+
print:
|
|
196
|
+
|
|
197
|
+
| Tool | Used for |
|
|
198
|
+
| --- | --- |
|
|
199
|
+
| `ps` | identity, state, priority, start time, elapsed and CPU time, argv, environment |
|
|
200
|
+
| `lsof` | open files, sockets, working directory, handle count |
|
|
201
|
+
| `pgrep` | name matching and children |
|
|
202
|
+
| `sysctl` | boot time, to place the start time in context |
|
|
203
|
+
| `codesign` | signing authority, Team ID, signing date, entitlements |
|
|
204
|
+
| `spctl` | the Gatekeeper verdict (assessed on the app bundle, not the inner executable) |
|
|
205
|
+
| `xattr`, `pkgutil`, `file` | quarantine flag, installing package, architectures |
|
|
206
|
+
| `launchctl` | whether it's a managed job |
|
|
207
|
+
|
|
208
|
+
Two consequences worth knowing:
|
|
209
|
+
|
|
210
|
+
- **Environment variables need root.** macOS only exposes another process's
|
|
211
|
+
environment to root, even your own processes. `sudo pidlens PID` shows them.
|
|
212
|
+
Names that look like credentials (`*_TOKEN`, `*_SECRET`, `*PASSWORD*`, …) are
|
|
213
|
+
masked before printing either way.
|
|
214
|
+
- **Gatekeeper "not assessed" is normal.** `spctl` only rates app bundles, so a
|
|
215
|
+
plain executable comes back unassessable even when its signature is perfectly
|
|
216
|
+
valid. pidlens says that, instead of reporting a scary "rejected".
|
|
217
|
+
|
|
218
|
+
## Requirements
|
|
219
|
+
|
|
220
|
+
macOS (tested on Sonoma and Sequoia) and Python 3.8 or newer. No third-party
|
|
221
|
+
dependencies — the whole tool is the standard library plus the system tools
|
|
222
|
+
above.
|
|
223
|
+
|
|
224
|
+
## Development
|
|
225
|
+
|
|
226
|
+
```sh
|
|
227
|
+
make venv # local .venv with dev dependencies
|
|
228
|
+
make test # 550+ tests, no live processes required for most of them
|
|
229
|
+
make demo # run pidlens against your shell
|
|
230
|
+
make build # sdist + wheel
|
|
231
|
+
make npm-pack # build the npm tarball
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
The code is split so that almost all of it is testable without a running
|
|
235
|
+
process:
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
src/pidlens/
|
|
239
|
+
humanize.py bytes, durations, ps state codes → English (pure)
|
|
240
|
+
layout.py width-aware wrapping and the Renderer (pure)
|
|
241
|
+
parse.py one parser per tool's output format (pure)
|
|
242
|
+
system.py the only module that shells out
|
|
243
|
+
collect.py builds the data model for one pid
|
|
244
|
+
report.py turns the data model into sections
|
|
245
|
+
cli.py arguments, exit codes, JSON
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Tests use fixtures captured from real `lsof`, `codesign`, `spctl` and
|
|
249
|
+
`launchctl` output, plus a `FakeRunner` that answers commands from a dict — so
|
|
250
|
+
the parsers and the entire report are verified without touching a live process.
|
|
251
|
+
The layout suite asserts that **no line ever exceeds the terminal width** and
|
|
252
|
+
that wrapping loses no characters, across widths from 38 to 110 columns.
|
|
253
|
+
|
|
254
|
+
## Contributing
|
|
255
|
+
|
|
256
|
+
Issues and pull requests are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
MIT © Sayed Tauseef Naqvi
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pidlens"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Everything macOS knows about a running process, in one readable report."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Sayed Tauseef Naqvi", email = "tauseef.naqvi786@gmail.com" }]
|
|
13
|
+
keywords = ["macos", "process", "ps", "lsof", "pid", "cli", "sysadmin",
|
|
14
|
+
"debugging", "forensics", "codesign", "launchd"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Environment :: Console",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Intended Audience :: System Administrators",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Operating System :: MacOS :: MacOS X",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.8",
|
|
24
|
+
"Programming Language :: Python :: 3.9",
|
|
25
|
+
"Programming Language :: Python :: 3.10",
|
|
26
|
+
"Programming Language :: Python :: 3.11",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Programming Language :: Python :: 3.13",
|
|
29
|
+
"Topic :: System :: Monitoring",
|
|
30
|
+
"Topic :: System :: Systems Administration",
|
|
31
|
+
"Topic :: Utilities",
|
|
32
|
+
]
|
|
33
|
+
dependencies = []
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/sthnaqvi/pidlens"
|
|
37
|
+
Repository = "https://github.com/sthnaqvi/pidlens"
|
|
38
|
+
Issues = "https://github.com/sthnaqvi/pidlens/issues"
|
|
39
|
+
Changelog = "https://github.com/sthnaqvi/pidlens/blob/main/CHANGELOG.md"
|
|
40
|
+
|
|
41
|
+
[project.scripts]
|
|
42
|
+
pidlens = "pidlens.cli:main"
|
|
43
|
+
|
|
44
|
+
[project.optional-dependencies]
|
|
45
|
+
dev = ["pytest>=7", "build>=1.0", "twine>=5.0"]
|
|
46
|
+
|
|
47
|
+
[tool.setuptools]
|
|
48
|
+
package-dir = { "" = "src" }
|
|
49
|
+
|
|
50
|
+
[tool.setuptools.packages.find]
|
|
51
|
+
where = ["src"]
|
|
52
|
+
|
|
53
|
+
[tool.pytest.ini_options]
|
|
54
|
+
testpaths = ["tests"]
|
|
55
|
+
addopts = "-q"
|
pidlens-0.1.0/setup.cfg
ADDED