feat: control API and Tor integration #1

Merged
username merged 9 commits from feat/control-api into main 2026-02-17 09:56:06 +00:00
4 changed files with 31 additions and 2 deletions
Showing only changes of commit 6c84a144c0 - Show all commits

View File

@@ -110,6 +110,7 @@ Options:
-v, --verbose Debug logging
-q, --quiet Errors only
--cprofile [FILE] Enable cProfile, dump to FILE (default: s5p.prof)
--tracemalloc [N] Enable tracemalloc, show top N allocators on exit (default: 10)
-V, --version Show version
```

View File

@@ -17,6 +17,8 @@ s5p -m 512 # max concurrent connections
s5p --api 127.0.0.1:1081 # enable control API
s5p --cprofile # profile to s5p.prof
s5p --cprofile out.prof # profile to custom file
s5p --tracemalloc # memory profile (top 10)
s5p --tracemalloc 20 # memory profile (top 20)
```
## Container

View File

@@ -419,6 +419,15 @@ s5p --cprofile output.prof -c config/s5p.yaml
# Analyze after stopping
python -m pstats s5p.prof
# Memory profiling with tracemalloc (top 10 allocators on exit)
s5p --tracemalloc -c config/s5p.yaml
# Show top 20 allocators
s5p --tracemalloc 20 -c config/s5p.yaml
# Both profilers simultaneously
s5p --cprofile --tracemalloc -c config/s5p.yaml
```
## Testing the Proxy

View File

@@ -62,6 +62,10 @@ def _parse_args(argv: list[str] | None = None) -> argparse.Namespace:
"--cprofile", metavar="FILE", nargs="?", const="s5p.prof",
help="enable cProfile, dump stats to FILE (default: s5p.prof)",
)
p.add_argument(
"--tracemalloc", metavar="N", nargs="?", const=10, type=int,
help="enable tracemalloc, show top N allocators on exit (default: 10)",
)
return p.parse_args(argv)
@@ -112,6 +116,11 @@ def main(argv: list[str] | None = None) -> int:
config.log_level = "error"
_setup_logging(config.log_level)
logger = logging.getLogger("s5p")
if args.tracemalloc:
import tracemalloc
tracemalloc.start()
try:
if args.cprofile:
@@ -123,13 +132,21 @@ def main(argv: list[str] | None = None) -> int:
finally:
prof.disable()
prof.dump_stats(args.cprofile)
logging.getLogger("s5p").info("profile saved to %s", args.cprofile)
logger.info("profile saved to %s", args.cprofile)
else:
asyncio.run(serve(config))
except KeyboardInterrupt:
return 0
except Exception as e:
logging.getLogger("s5p").error("%s", e)
logger.error("%s", e)
return 1
finally:
if args.tracemalloc:
import tracemalloc
snapshot = tracemalloc.take_snapshot()
stats = snapshot.statistics("lineno")
logger.info("tracemalloc: top %d allocations", args.tracemalloc)
for stat in stats[:args.tracemalloc]:
logger.info(" %s", stat)
return 0