peeka 0.1.1__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.
- peeka-0.1.1/PKG-INFO +11 -0
- peeka-0.1.1/README.md +281 -0
- peeka-0.1.1/peeka/__init__.py +2 -0
- peeka-0.1.1/peeka/cli/__init__.py +2 -0
- peeka-0.1.1/peeka/cli/main.py +1300 -0
- peeka-0.1.1/peeka/commands/__init__.py +0 -0
- peeka-0.1.1/peeka/commands/base.py +46 -0
- peeka-0.1.1/peeka/commands/complete.py +182 -0
- peeka-0.1.1/peeka/commands/detach.py +46 -0
- peeka-0.1.1/peeka/commands/logger.py +153 -0
- peeka-0.1.1/peeka/commands/memory.py +642 -0
- peeka-0.1.1/peeka/commands/monitor.py +333 -0
- peeka-0.1.1/peeka/commands/reset.py +59 -0
- peeka-0.1.1/peeka/commands/search.py +304 -0
- peeka-0.1.1/peeka/commands/stack.py +125 -0
- peeka-0.1.1/peeka/commands/thread.py +282 -0
- peeka-0.1.1/peeka/commands/top.py +368 -0
- peeka-0.1.1/peeka/commands/trace.py +122 -0
- peeka-0.1.1/peeka/commands/vmtool.py +558 -0
- peeka-0.1.1/peeka/commands/watch.py +125 -0
- peeka-0.1.1/peeka/core/__init__.py +0 -0
- peeka-0.1.1/peeka/core/agent.py +258 -0
- peeka-0.1.1/peeka/core/attach.py +381 -0
- peeka-0.1.1/peeka/core/client.py +278 -0
- peeka-0.1.1/peeka/core/injector.py +1266 -0
- peeka-0.1.1/peeka/core/monitor.py +134 -0
- peeka-0.1.1/peeka/core/observer.py +245 -0
- peeka-0.1.1/peeka/core/output.py +74 -0
- peeka-0.1.1/peeka/core/safeeval/__init__.py +8 -0
- peeka-0.1.1/peeka/core/safeeval/simpleeval.py +931 -0
- peeka-0.1.1/peeka/tui/__init__.py +106 -0
- peeka-0.1.1/peeka/tui/__main__.py +9 -0
- peeka-0.1.1/peeka/tui/app.py +128 -0
- peeka-0.1.1/peeka/tui/completion.py +60 -0
- peeka-0.1.1/peeka/tui/screens/__init__.py +1 -0
- peeka-0.1.1/peeka/tui/screens/help.py +89 -0
- peeka-0.1.1/peeka/tui/screens/main.py +185 -0
- peeka-0.1.1/peeka/tui/screens/process_selector.py +185 -0
- peeka-0.1.1/peeka/tui/styles/peeka.tcss +582 -0
- peeka-0.1.1/peeka/tui/views/__init__.py +1 -0
- peeka-0.1.1/peeka/tui/views/dashboard.py +435 -0
- peeka-0.1.1/peeka/tui/views/inspect.py +162 -0
- peeka-0.1.1/peeka/tui/views/logger.py +194 -0
- peeka-0.1.1/peeka/tui/views/memory.py +1034 -0
- peeka-0.1.1/peeka/tui/views/monitor.py +322 -0
- peeka-0.1.1/peeka/tui/views/stack.py +367 -0
- peeka-0.1.1/peeka/tui/views/thread.py +295 -0
- peeka-0.1.1/peeka/tui/views/top.py +270 -0
- peeka-0.1.1/peeka/tui/views/trace.py +567 -0
- peeka-0.1.1/peeka/tui/views/watch.py +645 -0
- peeka-0.1.1/peeka/tui/widgets/__init__.py +1 -0
- peeka-0.1.1/peeka/tui/widgets/autocomplete_input.py +195 -0
- peeka-0.1.1/peeka/utils/__init__.py +0 -0
- peeka-0.1.1/peeka/utils/formatters.py +195 -0
- peeka-0.1.1/peeka/utils/patterns.py +96 -0
- peeka-0.1.1/peeka.egg-info/PKG-INFO +11 -0
- peeka-0.1.1/peeka.egg-info/SOURCES.txt +81 -0
- peeka-0.1.1/peeka.egg-info/dependency_links.txt +1 -0
- peeka-0.1.1/peeka.egg-info/entry_points.txt +3 -0
- peeka-0.1.1/peeka.egg-info/requires.txt +8 -0
- peeka-0.1.1/peeka.egg-info/top_level.txt +1 -0
- peeka-0.1.1/pyproject.toml +42 -0
- peeka-0.1.1/setup.cfg +4 -0
- peeka-0.1.1/tests/test_client_thread_safety.py +617 -0
- peeka-0.1.1/tests/test_client_unit.py +300 -0
- peeka-0.1.1/tests/test_compatibility.py +307 -0
- peeka-0.1.1/tests/test_detach_cmd.py +114 -0
- peeka-0.1.1/tests/test_injector.py +538 -0
- peeka-0.1.1/tests/test_integration.py +285 -0
- peeka-0.1.1/tests/test_logger_cmd.py +180 -0
- peeka-0.1.1/tests/test_memory.py +369 -0
- peeka-0.1.1/tests/test_monitor.py +302 -0
- peeka-0.1.1/tests/test_observer.py +141 -0
- peeka-0.1.1/tests/test_output_formatter.py +291 -0
- peeka-0.1.1/tests/test_reset.py +484 -0
- peeka-0.1.1/tests/test_search.py +262 -0
- peeka-0.1.1/tests/test_stack.py +257 -0
- peeka-0.1.1/tests/test_theme.py +260 -0
- peeka-0.1.1/tests/test_trace.py +355 -0
- peeka-0.1.1/tests/test_tui.py +279 -0
- peeka-0.1.1/tests/test_utils_formatters.py +286 -0
- peeka-0.1.1/tests/test_utils_patterns.py +163 -0
- peeka-0.1.1/tests/test_vmtool.py +575 -0
peeka-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: peeka
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: A Python dynamic debugger inspired by Arthas, using Python 3.14's safe external debugger interface
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Provides-Extra: tui
|
|
7
|
+
Requires-Dist: textual>=7.5.0; extra == "tui"
|
|
8
|
+
Provides-Extra: dev
|
|
9
|
+
Requires-Dist: textual>=7.5.0; extra == "dev"
|
|
10
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
11
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
peeka-0.1.1/README.md
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
<p align="right">
|
|
2
|
+
<a href="README.zh-CN.md">中文</a> | <strong>English</strong>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# Peeka
|
|
6
|
+
|
|
7
|
+
Runtime diagnostic tool for Python applications, inspired by [Alibaba Arthas](https://github.com/alibaba/arthas). Non-invasive function observation with zero code changes.
|
|
8
|
+
|
|
9
|
+
Uses [PEP 768](https://peps.python.org/pep-0768/) (`sys.remote_exec`) on Python 3.14+, with a GDB + ptrace fallback for Python 3.9–3.13.
|
|
10
|
+
|
|
11
|
+
## Key Features
|
|
12
|
+
|
|
13
|
+
- **Non-invasive** — Inject observation logic at runtime, fully restored on detach
|
|
14
|
+
- **Real-time streaming** — Millisecond-latency data via Unix domain sockets
|
|
15
|
+
- **Production-safe** — < 5% overhead, fixed-size memory buffers, graceful error recovery
|
|
16
|
+
- **Secure filtering** — [simpleeval](https://github.com/danthedeckie/simpleeval)-based condition expressions, blocks all code injection
|
|
17
|
+
- **Dual interface** — CLI (JSONL output, pipe-friendly) and interactive TUI
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
### Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install peeka # CLI only
|
|
25
|
+
pip install peeka[tui] # CLI + TUI
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Basic Usage
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# 1. Attach to a running Python process
|
|
32
|
+
peeka-cli attach <pid>
|
|
33
|
+
|
|
34
|
+
# 2. Watch function calls
|
|
35
|
+
peeka-cli watch "module.Class.method" -n 5
|
|
36
|
+
|
|
37
|
+
# 3. Watch with condition filter
|
|
38
|
+
peeka-cli watch "module.func" --condition "params[0] > 100"
|
|
39
|
+
|
|
40
|
+
# 4. Trace call tree with timing
|
|
41
|
+
peeka-cli trace "module.func" -d 3
|
|
42
|
+
|
|
43
|
+
# 5. Capture call stack
|
|
44
|
+
peeka-cli stack "module.func" -n 3
|
|
45
|
+
|
|
46
|
+
# 6. Launch TUI
|
|
47
|
+
peeka
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Pipe-Friendly Output (JSONL)
|
|
51
|
+
|
|
52
|
+
All CLI output is JSONL — one JSON object per line with a `type` field:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Filter observations with jq
|
|
56
|
+
peeka-cli watch "module.func" | jq 'select(.type == "observation")'
|
|
57
|
+
|
|
58
|
+
# Find slow calls
|
|
59
|
+
peeka-cli watch "module.func" | jq 'select(.type == "observation" and .data.duration_ms > 100)'
|
|
60
|
+
|
|
61
|
+
# Save to file
|
|
62
|
+
peeka-cli watch "module.func" > observations.jsonl
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Commands
|
|
66
|
+
|
|
67
|
+
| Command | Description |
|
|
68
|
+
|-----------|---------------------------------------------|
|
|
69
|
+
| `attach` | Attach to a running Python process |
|
|
70
|
+
| `watch` | Observe function calls (args, return, time) |
|
|
71
|
+
| `trace` | Trace call tree with timing breakdown |
|
|
72
|
+
| `stack` | Capture call stack at function entry |
|
|
73
|
+
| `monitor` | Periodic performance statistics |
|
|
74
|
+
| `logger` | Adjust log levels at runtime |
|
|
75
|
+
| `memory` | Memory usage analysis |
|
|
76
|
+
| `inspect` | Runtime object inspection |
|
|
77
|
+
| `sc`/`sm` | Search classes / search methods |
|
|
78
|
+
| `reset` | Remove all injected enhancements |
|
|
79
|
+
| `detach` | Detach from target process |
|
|
80
|
+
|
|
81
|
+
### watch
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
peeka-cli watch <pattern> [options]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
| Option | Description | Default |
|
|
88
|
+
|--------------------|-----------------------------------------|---------|
|
|
89
|
+
| `-x, --depth` | Output depth for nested objects | 2 |
|
|
90
|
+
| `-n, --times` | Number of observations (-1 = infinite) | -1 |
|
|
91
|
+
| `--condition` | Filter expression (supports `cost` var) | — |
|
|
92
|
+
| `-b, --before` | Observe at function entry | false |
|
|
93
|
+
| `-s, --success` | Observe on success only | false |
|
|
94
|
+
| `-e, --exception` | Observe on exception only | false |
|
|
95
|
+
| `-f, --finish` | Observe on both success and exception | true |
|
|
96
|
+
|
|
97
|
+
**Pattern format**: `module.Class.method` or `module.function`
|
|
98
|
+
|
|
99
|
+
### trace
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
peeka-cli trace <pattern> [options]
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
| Option | Description | Default |
|
|
106
|
+
|--------------------|--------------------------------------------|---------|
|
|
107
|
+
| `-d, --depth` | Max call tree depth | 3 |
|
|
108
|
+
| `-n, --times` | Number of observations (-1 = infinite) | -1 |
|
|
109
|
+
| `--condition` | Filter expression (supports `cost` var) | — |
|
|
110
|
+
| `--skip-builtin` | Skip stdlib/built-in functions | true |
|
|
111
|
+
| `--min-duration` | Minimum duration filter (ms) | 0 |
|
|
112
|
+
|
|
113
|
+
**Output example:**
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
`---[125.3ms] calculator.Calculator.calculate()
|
|
117
|
+
+---[2.1ms] calculator.Calculator._validate()
|
|
118
|
+
+---[98.2ms] calculator.Calculator._compute()
|
|
119
|
+
| `---[95.1ms] math.sqrt()
|
|
120
|
+
`---[15.7ms] calculator.Logger.info()
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### stack
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
peeka-cli stack <pattern> [options]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
| Option | Description | Default |
|
|
130
|
+
|--------------------|--------------------------------------------|---------|
|
|
131
|
+
| `-n, --times` | Number of captures (-1 = infinite) | -1 |
|
|
132
|
+
| `--condition` | Filter expression | — |
|
|
133
|
+
| `--depth` | Stack depth limit | 10 |
|
|
134
|
+
|
|
135
|
+
## Condition Expressions
|
|
136
|
+
|
|
137
|
+
Powered by [simpleeval](https://github.com/danthedeckie/simpleeval) for safe evaluation:
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
params[0] > 100 # Positional argument check
|
|
141
|
+
len(params) > 2 # Argument count
|
|
142
|
+
kwargs.get('debug') == True # Keyword argument check
|
|
143
|
+
cost > 50 # Execution time in ms (watch/trace)
|
|
144
|
+
str(x).startswith('prefix') # String operations
|
|
145
|
+
x + y > 10 # Arithmetic
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Security**: Only safe operations (comparisons, arithmetic, logic) are allowed. `eval`, `exec`, `__import__`, `open`, `compile` and reflection via `__class__`/`__subclasses__` are all blocked.
|
|
149
|
+
|
|
150
|
+
## Output Format
|
|
151
|
+
|
|
152
|
+
Every line is a JSON object with a `type` field:
|
|
153
|
+
|
|
154
|
+
| Type | Description | Commands |
|
|
155
|
+
|---------------|-------------------------------------|------------------------|
|
|
156
|
+
| `status` | Progress/info messages | attach |
|
|
157
|
+
| `success` | Command completed successfully | attach, detach |
|
|
158
|
+
| `error` | Command failed | all |
|
|
159
|
+
| `event` | Control events (started/stopped) | watch, stack, monitor |
|
|
160
|
+
| `observation` | Real-time observation data | watch, stack, monitor |
|
|
161
|
+
| `result` | Query results (non-streaming) | logger, memory, sc, sm |
|
|
162
|
+
|
|
163
|
+
<details>
|
|
164
|
+
<summary>Output examples</summary>
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{"type": "status", "level": "info", "message": "Attaching to process 12345"}
|
|
168
|
+
{"type": "success", "command": "attach", "data": {"pid": 12345, "socket": "/tmp/peeka_xxx.sock"}}
|
|
169
|
+
{"type": "event", "event": "watch_started", "data": {"watch_id": "watch_001", "pattern": "module.func"}}
|
|
170
|
+
{
|
|
171
|
+
"type": "observation",
|
|
172
|
+
"watch_id": "watch_001",
|
|
173
|
+
"timestamp": 1705586200.123,
|
|
174
|
+
"func_name": "demo.Calculator.add",
|
|
175
|
+
"args": [1, 2],
|
|
176
|
+
"kwargs": {},
|
|
177
|
+
"result": 3,
|
|
178
|
+
"success": true,
|
|
179
|
+
"duration_ms": 0.123,
|
|
180
|
+
"count": 1
|
|
181
|
+
}
|
|
182
|
+
{"type": "error", "command": "watch", "error": "Cannot find target: invalid.pattern"}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
</details>
|
|
186
|
+
|
|
187
|
+
## Python Version Support
|
|
188
|
+
|
|
189
|
+
| Python Version | Attach Mechanism | Requirements |
|
|
190
|
+
|----------------|-------------------------------|-----------------------------------|
|
|
191
|
+
| 3.14+ | PEP 768 `sys.remote_exec()` | Same UID or `CAP_SYS_PTRACE` |
|
|
192
|
+
| 3.9–3.13 | GDB + ptrace fallback | GDB 7.3+, ptrace_scope ≤ 1 |
|
|
193
|
+
|
|
194
|
+
### Python < 3.14 Setup
|
|
195
|
+
|
|
196
|
+
GDB is required. Debug symbols are **strongly recommended** — some distros include them by default, but if GDB reports "no symbol" errors, install them:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
# Debian/Ubuntu
|
|
200
|
+
sudo apt-get install gdb python3-dbg
|
|
201
|
+
|
|
202
|
+
# RHEL/Fedora
|
|
203
|
+
sudo yum install gdb python3-debuginfo
|
|
204
|
+
|
|
205
|
+
# Arch
|
|
206
|
+
sudo pacman -S gdb
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Docker
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
docker run --cap-add=SYS_PTRACE your-image
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### ptrace Restrictions
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
# Check current setting
|
|
219
|
+
cat /proc/sys/kernel/yama/ptrace_scope
|
|
220
|
+
|
|
221
|
+
# Temporarily allow (for testing)
|
|
222
|
+
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
|
|
223
|
+
|
|
224
|
+
# SELinux (Fedora/RHEL)
|
|
225
|
+
sudo setsebool -P deny_ptrace=off
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Environment Variables
|
|
229
|
+
|
|
230
|
+
| Variable | Description | Default |
|
|
231
|
+
|----------------------|------------------------------|----------|
|
|
232
|
+
| `PEEKA_SOCKET_DIR` | Socket file directory | `/tmp` |
|
|
233
|
+
| `PEEKA_TIMEOUT` | Command timeout (seconds) | `30` |
|
|
234
|
+
| `PEEKA_BUFFER_SIZE` | Observation buffer size | `10000` |
|
|
235
|
+
|
|
236
|
+
## Troubleshooting
|
|
237
|
+
|
|
238
|
+
### Attach fails (permission denied)
|
|
239
|
+
|
|
240
|
+
- Ensure same UID or `CAP_SYS_PTRACE`
|
|
241
|
+
- Check `ptrace_scope` (see above)
|
|
242
|
+
- For GDB fallback: install debug symbols if "no symbol" error appears
|
|
243
|
+
|
|
244
|
+
### No observation data
|
|
245
|
+
|
|
246
|
+
- Verify function name (use fully qualified name: `module.Class.method`)
|
|
247
|
+
- Confirm the function is actually being called
|
|
248
|
+
- Check if condition expression is too restrictive
|
|
249
|
+
|
|
250
|
+
### Target process behaving abnormally
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# Stop observation
|
|
254
|
+
peeka-cli watch --action stop <watch_id>
|
|
255
|
+
|
|
256
|
+
# If issues persist, restart target process
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Architecture
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
CLI/TUI → AgentClient → Unix Socket → PeekaAgent (injected in target)
|
|
263
|
+
├─ CommandRouter → BaseCommand subclasses
|
|
264
|
+
├─ DecoratorInjector (function wrapping)
|
|
265
|
+
└─ ObservationManager (buffered streaming)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
- **Attach**: PEP 768 `sys.remote_exec()` on 3.14+, GDB + ptrace on 3.9–3.13
|
|
269
|
+
- **Observe**: Decorator injection wraps target functions, captures args/return/exceptions/timing
|
|
270
|
+
- **Stream**: Real-time observation data via Unix domain socket (length-prefixed JSON)
|
|
271
|
+
- **Commands**: Modular `BaseCommand` subclasses, registered in `PeekaAgent._register_handlers()`
|
|
272
|
+
|
|
273
|
+
## License
|
|
274
|
+
|
|
275
|
+
MIT License
|
|
276
|
+
|
|
277
|
+
## Acknowledgments
|
|
278
|
+
|
|
279
|
+
- Inspired by [Alibaba Arthas](https://github.com/alibaba/arthas)
|
|
280
|
+
- Safe evaluation: [simpleeval](https://github.com/danthedeckie/simpleeval)
|
|
281
|
+
- Remote debugging protocol: [PEP 768](https://peps.python.org/pep-0768/)
|