99
1010
1111MS_WINDOWS = (sys .platform == "win32" )
12+ COMMAND_TIMEOUT = 60.0
1213
1314
1415def normalize_text (text ):
@@ -293,6 +294,7 @@ def format_groups(groups):
293294 "BUILDPYTHON" ,
294295 "CC" ,
295296 "CFLAGS" ,
297+ "CI" ,
296298 "COLUMNS" ,
297299 "COMPUTERNAME" ,
298300 "COMSPEC" ,
@@ -355,6 +357,9 @@ def format_groups(groups):
355357 "_PYTHON_SYSCONFIGDATA_PATH" ,
356358 "__PYVENV_LAUNCHER__" ,
357359
360+ # Lower case variables
361+ "container" ,
362+
358363 # Sanitizer options
359364 "ASAN_OPTIONS" ,
360365 "LSAN_OPTIONS" ,
@@ -434,19 +439,40 @@ def format_attr(attr, value):
434439 info_add ('readline.library' , 'GNU readline' )
435440
436441
437- def collect_gdb ( info_add ):
442+ def run_command ( cmd , ** kwargs ):
438443 import subprocess
444+ timeout = COMMAND_TIMEOUT
439445
440446 try :
441- proc = subprocess .Popen ([ "gdb" , "-nx" , "--version" ] ,
447+ proc = subprocess .Popen (cmd ,
442448 stdout = subprocess .PIPE ,
443- stderr = subprocess .PIPE ,
444- universal_newlines = True )
445- version = proc .communicate ()[0 ]
449+ stderr = subprocess .DEVNULL ,
450+ text = True ,
451+ ** kwargs )
452+ # ignore stderr
453+ with proc :
454+ try :
455+ stdout = proc .communicate (timeout = timeout )[0 ]
456+ except :
457+ proc .kill ()
458+ proc .communicate ()
459+ raise
460+
446461 if proc .returncode :
447- # ignore gdb failure: test_gdb will log the error
448- return
462+ return ''
463+
464+ # Strip trailing spaces and newlines
465+ return stdout .rstrip ()
449466 except OSError :
467+ return ''
468+ except subprocess .TimeoutExpired :
469+ print (f"ERROR: Command { ' ' .join (cmd )} : timeout!" )
470+ return ''
471+
472+
473+ def collect_gdb (info_add ):
474+ version = run_command (["gdb" , "-nx" , "--version" ])
475+ if not version :
450476 return
451477
452478 # Only keep the first line
@@ -847,7 +873,6 @@ def collect_support_threading_helper(info_add):
847873
848874
849875def collect_cc (info_add ):
850- import subprocess
851876 import sysconfig
852877
853878 CC = sysconfig .get_config_var ('CC' )
@@ -860,19 +885,13 @@ def collect_cc(info_add):
860885 except ImportError :
861886 args = CC .split ()
862887 args .append ('--version' )
863- try :
864- proc = subprocess .Popen (args ,
865- stdout = subprocess .PIPE ,
866- stderr = subprocess .STDOUT ,
867- universal_newlines = True )
868- except OSError :
888+
889+ stdout = run_command (args )
890+ if not stdout :
869891 # Cannot run the compiler, for example when Python has been
870892 # cross-compiled and installed on the target platform where the
871893 # compiler is missing.
872- return
873-
874- stdout = proc .communicate ()[0 ]
875- if proc .returncode :
894+ #
876895 # CC --version failed: ignore error
877896 return
878897
@@ -978,21 +997,11 @@ def collect_windows(info_add):
978997 call_func (info_add , 'windows.oem_code_page' , _winapi , 'GetOEMCP' )
979998
980999 # windows.version_caption: "wmic os get Caption,Version /value" command
981- import subprocess
982- try :
983- # When wmic.exe output is redirected to a pipe,
984- # it uses the OEM code page
985- proc = subprocess .Popen (["wmic" , "os" , "get" , "Caption,Version" , "/value" ],
986- stdout = subprocess .PIPE ,
987- stderr = subprocess .PIPE ,
988- encoding = "oem" ,
989- text = True )
990- output , stderr = proc .communicate ()
991- if proc .returncode :
992- output = ""
993- except OSError :
994- pass
995- else :
1000+ output = run_command (["wmic" , "os" , "get" , "Caption,Version" , "/value" ],
1001+ # When wmic.exe output is redirected to a pipe,
1002+ # it uses the OEM code page
1003+ encoding = "oem" )
1004+ if output :
9961005 for line in output .splitlines ():
9971006 line = line .strip ()
9981007 if line .startswith ('Caption=' ):
@@ -1005,23 +1014,11 @@ def collect_windows(info_add):
10051014 info_add ('windows.version' , line )
10061015
10071016 # windows.ver: "ver" command
1008- try :
1009- proc = subprocess .Popen (["ver" ], shell = True ,
1010- stdout = subprocess .PIPE ,
1011- stderr = subprocess .PIPE ,
1012- text = True )
1013- output = proc .communicate ()[0 ]
1014- if proc .returncode == 0xc0000142 :
1015- return
1016- if proc .returncode :
1017- output = ""
1018- except OSError :
1019- return
1020- else :
1021- output = output .strip ()
1022- line = output .splitlines ()[0 ]
1023- if line :
1024- info_add ('windows.ver' , line )
1017+ output = run_command (["ver" ], shell = True )
1018+ if output :
1019+ first_line = output .splitlines ()[0 ]
1020+ if first_line :
1021+ info_add ('windows.ver' , first_line )
10251022
10261023 # windows.developer_mode: get AllowDevelopmentWithoutDevLicense registry
10271024 value = winreg_query (r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows"
@@ -1132,7 +1129,40 @@ def get_machine_id():
11321129 return None
11331130
11341131
1135- def collect_linux (info_add ):
1132+ def detect_virt ():
1133+ # Run systemd-detect-virt command
1134+ virt = run_command (["systemd-detect-virt" ])
1135+ if virt and virt != "none" :
1136+ return virt
1137+
1138+ # Check if the process in running in a container
1139+ import os .path
1140+ if os .path .exists ('/.dockerenv' ):
1141+ return 'docker'
1142+ if os .path .exists ('/run/.containerenv' ):
1143+ return 'podman'
1144+
1145+ container = read_first_line ('/run/systemd/container' )
1146+ if container :
1147+ return container
1148+
1149+ # Other ways to check if running in a container:
1150+ # * Parse /proc/1/mounts or /proc/1/mountinfo (check "/" filesystem).
1151+ # * Parse /proc/1/cgroup.
1152+ # * Parse the first line of /proc/1/sched (check process name is different
1153+ # than "init" and "systemd").
1154+ # * Check / inode.
1155+ # * On systems using SELinux (Fedora/CentOS/RHEL), check for "container_t"
1156+ # label, for example of /proc/1/attr/current.
1157+ # * Check for "container" variable in /proc/1/environ
1158+ # (only root can read this file).
1159+ # * Check for "container" environment variable.
1160+ # * Set a specific env var when creating the container image.
1161+ # * Run virt-what, need to install the script, and must be run as root.
1162+ # * Check for "GITHUB_ACTIONS" environmant variable (GitHub Action).
1163+
1164+
1165+ def collect_system (info_add ):
11361166 boot_id = read_first_line ("/proc/sys/kernel/random/boot_id" )
11371167 if boot_id :
11381168 info_add ('system.boot_id' , boot_id )
@@ -1152,6 +1182,10 @@ def collect_linux(info_add):
11521182 uptime = f'{ uptime } sec'
11531183 info_add ('system.uptime' , uptime )
11541184
1185+ virt = detect_virt ()
1186+ if virt :
1187+ info_add ('system.virt' , virt )
1188+
11551189
11561190def collect_info (info ):
11571191 error = False
@@ -1194,7 +1228,7 @@ def collect_info(info):
11941228 collect_zlib ,
11951229 collect_zstd ,
11961230 collect_libregrtest_utils ,
1197- collect_linux ,
1231+ collect_system ,
11981232
11991233 # Collecting from tests should be last as they have side effects.
12001234 collect_test_socket ,
0 commit comments