99
1010
1111MS_WINDOWS = (sys .platform == "win32" )
12+ APPLE = (sys .platform in ("darwin" , "ios" , "tvos" , "watchos" ))
13+
14+ COMMAND_TIMEOUT = 60.0
1215
1316
1417def normalize_text (text ):
@@ -19,6 +22,16 @@ def normalize_text(text):
1922 return text .strip ()
2023
2124
25+ def first_line (text ):
26+ # Get the first line. Return text unchanged if it's empty.
27+ lines = text .splitlines ()
28+ if lines :
29+ return lines [0 ]
30+ else :
31+ # text is an empty string
32+ return text
33+
34+
2235def read_first_line (filename ):
2336 # Get the first line of a text file and strip trailing spaces
2437 try :
@@ -291,9 +304,11 @@ def format_groups(groups):
291304 "BUILDPYTHON" ,
292305 "CC" ,
293306 "CFLAGS" ,
307+ "CI" ,
294308 "COLUMNS" ,
295309 "COMPUTERNAME" ,
296310 "COMSPEC" ,
311+ "CONTAINER" ,
297312 "CPP" ,
298313 "CPPFLAGS" ,
299314 "DISPLAY" ,
@@ -308,6 +323,7 @@ def format_groups(groups):
308323 "HOMEDRIVE" ,
309324 "HOMEPATH" ,
310325 "IDLESTARTUP" ,
326+ "IMAGE_OS_VERSION" ,
311327 "IPHONEOS_DEPLOYMENT_TARGET" ,
312328 "LANG" ,
313329 "LDFLAGS" ,
@@ -432,24 +448,47 @@ def format_attr(attr, value):
432448 info_add ('readline.library' , 'GNU readline' )
433449
434450
435- def collect_gdb ( info_add ):
451+ def run_command ( cmd , check = True , ** kwargs ):
436452 import subprocess
453+ timeout = COMMAND_TIMEOUT
437454
455+ cmd_str = ' ' .join (cmd )
438456 try :
439- proc = subprocess .Popen ([ "gdb" , "-nx" , "--version" ] ,
457+ proc = subprocess .Popen (cmd ,
440458 stdout = subprocess .PIPE ,
441- stderr = subprocess .PIPE ,
442- universal_newlines = True )
443- version = proc .communicate ()[0 ]
444- if proc .returncode :
445- # ignore gdb failure: test_gdb will log the error
446- return
447- except OSError :
448- return
459+ stderr = subprocess .DEVNULL ,
460+ text = True ,
461+ ** kwargs )
462+ with proc :
463+ try :
464+ stdout = proc .communicate (timeout = timeout )[0 ]
465+ except :
466+ proc .kill ()
467+ proc .communicate ()
468+ raise
449469
450- # Only keep the first line
451- version = version .splitlines ()[0 ]
452- info_add ('gdb_version' , version )
470+ if check and proc .returncode :
471+ print (f"Command { cmd_str } failed with exit code { proc .returncode } " )
472+ return ''
473+
474+ # Strip trailing spaces and newlines
475+ stdout = stdout .rstrip ()
476+ return stdout
477+ except FileNotFoundError :
478+ return ''
479+ except OSError as exc :
480+ print (f"Command { cmd_str } failed with: { exc !r} " )
481+ return ''
482+ except subprocess .TimeoutExpired :
483+ print (f"Command { cmd_str } : timeout!" )
484+ return ''
485+
486+
487+ def collect_gdb (info_add ):
488+ version = run_command (["gdb" , "-nx" , "--version" ])
489+ if version :
490+ # Only keep the first line
491+ info_add ('gdb_version' , first_line (version ))
453492
454493
455494def collect_tkinter (info_add ):
@@ -833,7 +872,6 @@ def collect_support_threading_helper(info_add):
833872
834873
835874def collect_cc (info_add ):
836- import subprocess
837875 import sysconfig
838876
839877 CC = sysconfig .get_config_var ('CC' )
@@ -846,23 +884,17 @@ def collect_cc(info_add):
846884 except ImportError :
847885 args = CC .split ()
848886 args .append ('--version' )
849- try :
850- proc = subprocess .Popen (args ,
851- stdout = subprocess .PIPE ,
852- stderr = subprocess .STDOUT ,
853- universal_newlines = True )
854- except OSError :
887+
888+ stdout = run_command (args )
889+ if not stdout :
855890 # Cannot run the compiler, for example when Python has been
856891 # cross-compiled and installed on the target platform where the
857892 # compiler is missing.
858- return
859-
860- stdout = proc .communicate ()[0 ]
861- if proc .returncode :
893+ #
862894 # CC --version failed: ignore error
863895 return
864896
865- text = stdout . splitlines ()[ 0 ]
897+ text = first_line ( stdout )
866898 text = normalize_text (text )
867899 info_add ('CC.version' , text )
868900
@@ -957,21 +989,11 @@ def collect_windows(info_add):
957989 pass
958990
959991 # windows.version_caption: "wmic os get Caption,Version /value" command
960- import subprocess
961- try :
962- # When wmic.exe output is redirected to a pipe,
963- # it uses the OEM code page
964- proc = subprocess .Popen (["wmic" , "os" , "get" , "Caption,Version" , "/value" ],
965- stdout = subprocess .PIPE ,
966- stderr = subprocess .PIPE ,
967- encoding = "oem" ,
968- text = True )
969- output , stderr = proc .communicate ()
970- if proc .returncode :
971- output = ""
972- except OSError :
973- pass
974- else :
992+ output = run_command (["wmic" , "os" , "get" , "Caption,Version" , "/value" ],
993+ # When wmic.exe output is redirected to a pipe,
994+ # it uses the OEM code page
995+ encoding = "oem" )
996+ if output :
975997 for line in output .splitlines ():
976998 line = line .strip ()
977999 if line .startswith ('Caption=' ):
@@ -984,23 +1006,11 @@ def collect_windows(info_add):
9841006 info_add ('windows.version' , line )
9851007
9861008 # windows.ver: "ver" command
987- try :
988- proc = subprocess .Popen (["ver" ], shell = True ,
989- stdout = subprocess .PIPE ,
990- stderr = subprocess .PIPE ,
991- text = True )
992- output = proc .communicate ()[0 ]
993- if proc .returncode == 0xc0000142 :
994- return
995- if proc .returncode :
996- output = ""
997- except OSError :
998- return
999- else :
1000- output = output .strip ()
1001- line = output .splitlines ()[0 ]
1002- if line :
1003- info_add ('windows.ver' , line )
1009+ output = run_command (["ver" ], shell = True )
1010+ # "ver" output starts with an empty line: remove it
1011+ output = output .strip ()
1012+ if output :
1013+ info_add ('windows.ver' , first_line (output ))
10041014
10051015 # windows.developer_mode: get AllowDevelopmentWithoutDevLicense registry
10061016 value = winreg_query (r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows"
@@ -1111,7 +1121,45 @@ def get_machine_id():
11111121 return None
11121122
11131123
1114- def collect_linux (info_add ):
1124+ def detect_virt ():
1125+ # Run systemd-detect-virt command
1126+ virt = run_command (["systemd-detect-virt" ], check = False )
1127+ if virt and virt != "none" :
1128+ return virt
1129+
1130+ # Check if the process in running in a container
1131+ import os .path
1132+ if os .path .exists ('/.dockerenv' ):
1133+ return 'docker'
1134+ if os .path .exists ('/run/.containerenv' ):
1135+ return 'podman'
1136+
1137+ container = read_first_line ('/run/systemd/container' )
1138+ if container :
1139+ return container
1140+
1141+ if APPLE :
1142+ hv_vmm_present = run_command (['sysctl' , '-n' , 'kern.hv_vmm_present' ])
1143+ if hv_vmm_present == '1' :
1144+ return 'run in a VM (kern.hv_vmm_present is 1)'
1145+
1146+ # Other ways to check if running in a container:
1147+ # * Parse /proc/1/mounts or /proc/1/mountinfo (check "/" filesystem).
1148+ # * Parse /proc/1/cgroup.
1149+ # * Parse the first line of /proc/1/sched (check process name is different
1150+ # than "init" and "systemd").
1151+ # * Check / inode.
1152+ # * On systems using SELinux (Fedora/CentOS/RHEL), check for "container_t"
1153+ # label, for example of /proc/1/attr/current.
1154+ # * Check for "container" variable in /proc/1/environ
1155+ # (only root can read this file).
1156+ # * Check for "container" environment variable.
1157+ # * Set a specific env var when creating the container image.
1158+ # * Run virt-what, need to install the script, and must be run as root.
1159+ # * Check for "GITHUB_ACTIONS" environmant variable (GitHub Action).
1160+
1161+
1162+ def collect_system (info_add ):
11151163 boot_id = read_first_line ("/proc/sys/kernel/random/boot_id" )
11161164 if boot_id :
11171165 info_add ('system.boot_id' , boot_id )
@@ -1131,6 +1179,15 @@ def collect_linux(info_add):
11311179 uptime = f'{ uptime } sec'
11321180 info_add ('system.uptime' , uptime )
11331181
1182+ virt = detect_virt ()
1183+ if virt :
1184+ info_add ('system.virt' , virt )
1185+
1186+ if APPLE :
1187+ hardware = run_command (['sysctl' , '-n' , 'hw.model' ])
1188+ if hardware :
1189+ info_add ('system.hardware' , hardware )
1190+
11341191
11351192def collect_info (info ):
11361193 error = False
@@ -1172,7 +1229,7 @@ def collect_info(info):
11721229 collect_windows ,
11731230 collect_zlib ,
11741231 collect_libregrtest_utils ,
1175- collect_linux ,
1232+ collect_system ,
11761233
11771234 # Collecting from tests should be last as they have side effects.
11781235 collect_test_socket ,
0 commit comments