31
31
32
32
import configargparse
33
33
import humanfriendly
34
- from granulate_utils .linux .ns import is_running_in_init_pid
34
+ from granulate_utils .linux .ns import is_root , is_running_in_init_pid
35
35
from granulate_utils .linux .process import is_process_running
36
36
from granulate_utils .metadata .cloud import get_aws_execution_env
37
37
from psutil import NoSuchProcess , Process
70
70
atomically_symlink ,
71
71
get_iso8601_format_time ,
72
72
grab_gprofiler_mutex ,
73
- is_root ,
74
73
reset_umask ,
75
74
resource_path ,
76
75
run_process ,
77
76
)
78
- from gprofiler .utils .fs import escape_filename , mkdir_owned_root
77
+ from gprofiler .utils .fs import escape_filename , mkdir_owned_root_wrapper
79
78
from gprofiler .utils .proxy import get_https_proxy
80
79
81
80
if is_linux ():
@@ -121,6 +120,7 @@ def __init__(
121
120
output_dir : str ,
122
121
flamegraph : bool ,
123
122
rotating_output : bool ,
123
+ rootless : bool ,
124
124
profiler_api_client : Optional [ProfilerAPIClient ],
125
125
collect_metrics : bool ,
126
126
collect_metadata : bool ,
@@ -141,6 +141,7 @@ def __init__(
141
141
self ._output_dir = output_dir
142
142
self ._flamegraph = flamegraph
143
143
self ._rotating_output = rotating_output
144
+ self ._rootless = rootless
144
145
self ._profiler_api_client = profiler_api_client
145
146
self ._state = state
146
147
self ._remote_logs_handler = remote_logs_handler
@@ -566,6 +567,15 @@ def parse_cmd_args() -> configargparse.Namespace:
566
567
help = "Comma separated list of processes that will be filtered to profile,"
567
568
" given multiple times will append pids to one list" ,
568
569
)
570
+ parser .add_argument (
571
+ "--rootless" ,
572
+ action = "store_true" ,
573
+ default = False ,
574
+ help = "Run without root/sudo with limited functionality"
575
+ "Profiling is limted to only processes owned by this user that are passed with --pids. Logs and pid file "
576
+ "may be directed to user owned directory with --log-file and --pid-file respectively. Some additional "
577
+ "configuration (e.g. kernel.perf_event_paranoid) may be required to operate without root." ,
578
+ )
569
579
570
580
_add_profilers_arguments (parser )
571
581
@@ -903,8 +913,14 @@ def _add_profilers_arguments(parser: configargparse.ArgumentParser) -> None:
903
913
904
914
905
915
def verify_preconditions (args : configargparse .Namespace , processes_to_profile : Optional [List [Process ]]) -> None :
906
- if not is_root ():
907
- print ("Must run gprofiler as root, please re-run." , file = sys .stderr )
916
+ if not args .rootless and not is_root ():
917
+ print ("Not running as root, rerun with --rootless or as root." , file = sys .stderr )
918
+ sys .exit (1 )
919
+ elif args .rootless and is_root ():
920
+ print (
921
+ "Conflict, running with --rootless and as root, rerun with --rootless or as root (but not both)." ,
922
+ file = sys .stderr ,
923
+ )
908
924
sys .exit (1 )
909
925
910
926
if args .pid_ns_check and not is_running_in_init_pid ():
@@ -1102,7 +1118,7 @@ def main() -> None:
1102
1118
)
1103
1119
sys .exit (1 )
1104
1120
1105
- mkdir_owned_root (TEMPORARY_STORAGE_PATH )
1121
+ mkdir_owned_root_wrapper (TEMPORARY_STORAGE_PATH )
1106
1122
1107
1123
try :
1108
1124
client_kwargs = {}
@@ -1154,6 +1170,7 @@ def main() -> None:
1154
1170
output_dir = args .output_dir ,
1155
1171
flamegraph = args .flamegraph ,
1156
1172
rotating_output = args .rotating_output ,
1173
+ rootless = args .rootless ,
1157
1174
profiler_api_client = profiler_api_client ,
1158
1175
collect_metrics = args .collect_metrics ,
1159
1176
collect_metadata = args .collect_metadata ,
0 commit comments