Skip to content

Commit e8f768d

Browse files
committed
gh-138704: Add PermissionError messages to profiling.sampling
1 parent 074f3b2 commit e8f768d

File tree

1 file changed

+79
-36
lines changed

1 file changed

+79
-36
lines changed

Lib/profiling/sampling/sample.py

Lines changed: 79 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,44 @@
1414
from .pstats_collector import PstatsCollector
1515
from .stack_collector import CollapsedStackCollector
1616

17+
18+
MACOS_PERMISSION_ERROR = """\
19+
🔒 Tachyon was unable to access process memory due to insufficient permissions on Mac OS.
20+
To profile processes, you may need to run Tachyon with sudo. Note that System Integrity Protection (SIP) can block access to Python binaries in protected locations.
21+
Using a virtual environment or installing Python outside protected directories may help.
22+
"""
23+
24+
LINUX_PERMISSION_ERROR = """
25+
🔒 Tachyon was unable to acess process memory. This could be because tachyon
26+
has insufficient privileges (the required capability is CAP_SYS_PTRACE).
27+
Unprivileged processes cannot trace processes that they cannot send signals
28+
to or those running set-user-ID/set-group-ID programs, for security reasons.
29+
30+
If your uid matches the uid of the target process you want to analyze, you
31+
can do one of the following to get 'ptrace' scope permissions:
32+
33+
* If you are running inside a Docker container, you need to make sure you
34+
start the container using the '--cap-add=SYS_PTRACE' or '--privileged'
35+
command line arguments. Notice that this may not be enough if you are not
36+
running as 'root' inside the Docker container as you may need to disable
37+
hardening (see next points).
38+
39+
* Try running again with elevated permissions by running 'sudo -E !!'.
40+
41+
* You can disable kernel hardening for the current session temporarily (until
42+
a reboot happens) by running 'echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope'.
43+
"""
44+
45+
WINDOWS_PERMISSION_ERROR = """
46+
🔒 Tachyon requires administrator rights to access process memory on Windows.
47+
Please run your command prompt as Administrator and try again.
48+
"""
49+
50+
GENERIC_PERMISSION_ERROR = """
51+
🔒 Tachyon was unable to access the target process due to operating system restrictions or missing privileges.
52+
"""
53+
1754
_FREE_THREADED_BUILD = sysconfig.get_config_var("Py_GIL_DISABLED") is not None
18-
_MAX_STARTUP_ATTEMPTS = 5
19-
_STARTUP_RETRY_DELAY_SECONDS = 0.1
2055
_HELP_DESCRIPTION = """Sample a process's stack frames and generate profiling data.
2156
Supports the following target modes:
2257
- -p PID: Profile an existing process by PID
@@ -826,39 +861,47 @@ def main():
826861
elif target_count > 1:
827862
parser.error("only one target type can be specified: -p/--pid, -m/--module, or script")
828863

829-
if args.pid:
830-
sample(
831-
args.pid,
832-
sample_interval_usec=args.interval,
833-
duration_sec=args.duration,
834-
filename=args.outfile,
835-
all_threads=args.all_threads,
836-
limit=args.limit,
837-
sort=sort_value,
838-
show_summary=not args.no_summary,
839-
output_format=args.format,
840-
realtime_stats=args.realtime_stats,
841-
)
842-
elif args.module or args.args:
843-
if args.module:
844-
cmd = (sys.executable, "-m", args.module, *args.args)
845-
else:
846-
cmd = (sys.executable, *args.args)
847-
848-
# Use synchronized process startup
849-
process = _run_with_sync(cmd)
864+
try:
865+
if args.pid:
866+
sample(
867+
args.pid,
868+
sample_interval_usec=args.interval,
869+
duration_sec=args.duration,
870+
filename=args.outfile,
871+
all_threads=args.all_threads,
872+
limit=args.limit,
873+
sort=sort_value,
874+
show_summary=not args.no_summary,
875+
output_format=args.format,
876+
realtime_stats=args.realtime_stats,
877+
)
878+
elif args.module or args.args:
879+
if args.module:
880+
cmd = (sys.executable, "-m", args.module, *args.args)
881+
else:
882+
cmd = (sys.executable, *args.args)
850883

851-
# Process has already signaled readiness, start sampling immediately
852-
try:
853-
wait_for_process_and_sample(process.pid, sort_value, args)
854-
finally:
855-
if process.poll() is None:
856-
process.terminate()
857-
try:
858-
process.wait(timeout=2)
859-
except subprocess.TimeoutExpired:
860-
process.kill()
861-
process.wait()
884+
# Use synchronized process startup
885+
process = _run_with_sync(cmd)
862886

863-
if __name__ == "__main__":
864-
main()
887+
# Process has already signaled readiness, start sampling immediately
888+
try:
889+
wait_for_process_and_sample(process.pid, sort_value, args)
890+
finally:
891+
if process.poll() is None:
892+
process.terminate()
893+
try:
894+
process.wait(timeout=2)
895+
except subprocess.TimeoutExpired:
896+
process.kill()
897+
process.wait()
898+
except PermissionError:
899+
if sys.platform == "darwin":
900+
print(MACOS_PERMISSION_ERROR, file=sys.stderr)
901+
elif sys.platform.startswith("linux"):
902+
print(LINUX_PERMISSION_ERROR, file=sys.stderr)
903+
elif sys.platform.startswith("win"):
904+
print(WINDOWS_PERMISSION_ERROR, file=sys.stderr)
905+
else:
906+
print(GENERIC_PERMISSION_ERROR, file=sys.stderr)
907+
sys.exit(1)

0 commit comments

Comments
 (0)