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 :
@@ -293,9 +306,11 @@ def format_groups(groups):
293306 "BUILDPYTHON" ,
294307 "CC" ,
295308 "CFLAGS" ,
309+ "CI" ,
296310 "COLUMNS" ,
297311 "COMPUTERNAME" ,
298312 "COMSPEC" ,
313+ "CONTAINER" ,
299314 "CPP" ,
300315 "CPPFLAGS" ,
301316 "DISPLAY" ,
@@ -310,6 +325,7 @@ def format_groups(groups):
310325 "HOMEDRIVE" ,
311326 "HOMEPATH" ,
312327 "IDLESTARTUP" ,
328+ "IMAGE_OS_VERSION" ,
313329 "IPHONEOS_DEPLOYMENT_TARGET" ,
314330 "LANG" ,
315331 "LDFLAGS" ,
@@ -434,24 +450,47 @@ def format_attr(attr, value):
434450 info_add ('readline.library' , 'GNU readline' )
435451
436452
437- def collect_gdb ( info_add ):
453+ def run_command ( cmd , check = True , ** kwargs ):
438454 import subprocess
455+ timeout = COMMAND_TIMEOUT
439456
457+ cmd_str = ' ' .join (cmd )
440458 try :
441- proc = subprocess .Popen ([ "gdb" , "-nx" , "--version" ] ,
459+ proc = subprocess .Popen (cmd ,
442460 stdout = subprocess .PIPE ,
443- stderr = subprocess .PIPE ,
444- universal_newlines = True )
445- version = proc .communicate ()[0 ]
446- if proc .returncode :
447- # ignore gdb failure: test_gdb will log the error
448- return
449- except OSError :
450- return
461+ stderr = subprocess .DEVNULL ,
462+ text = True ,
463+ ** kwargs )
464+ with proc :
465+ try :
466+ stdout = proc .communicate (timeout = timeout )[0 ]
467+ except :
468+ proc .kill ()
469+ proc .communicate ()
470+ raise
451471
452- # Only keep the first line
453- version = version .splitlines ()[0 ]
454- info_add ('gdb_version' , version )
472+ if check and proc .returncode :
473+ print (f"Command { cmd_str } failed with exit code { proc .returncode } " )
474+ return ''
475+
476+ # Strip trailing spaces and newlines
477+ stdout = stdout .rstrip ()
478+ return stdout
479+ except FileNotFoundError :
480+ return ''
481+ except OSError as exc :
482+ print (f"Command { cmd_str } failed with: { exc !r} " )
483+ return ''
484+ except subprocess .TimeoutExpired :
485+ print (f"Command { cmd_str } : timeout!" )
486+ return ''
487+
488+
489+ def collect_gdb (info_add ):
490+ version = run_command (["gdb" , "-nx" , "--version" ])
491+ if version :
492+ # Only keep the first line
493+ info_add ('gdb_version' , first_line (version ))
455494
456495
457496def collect_tkinter (info_add ):
@@ -835,7 +874,6 @@ def collect_support_threading_helper(info_add):
835874
836875
837876def collect_cc (info_add ):
838- import subprocess
839877 import sysconfig
840878
841879 CC = sysconfig .get_config_var ('CC' )
@@ -848,23 +886,17 @@ def collect_cc(info_add):
848886 except ImportError :
849887 args = CC .split ()
850888 args .append ('--version' )
851- try :
852- proc = subprocess .Popen (args ,
853- stdout = subprocess .PIPE ,
854- stderr = subprocess .STDOUT ,
855- universal_newlines = True )
856- except OSError :
889+
890+ stdout = run_command (args )
891+ if not stdout :
857892 # Cannot run the compiler, for example when Python has been
858893 # cross-compiled and installed on the target platform where the
859894 # compiler is missing.
860- return
861-
862- stdout = proc .communicate ()[0 ]
863- if proc .returncode :
895+ #
864896 # CC --version failed: ignore error
865897 return
866898
867- text = stdout . splitlines ()[ 0 ]
899+ text = first_line ( stdout )
868900 text = normalize_text (text )
869901 info_add ('CC.version' , text )
870902
@@ -959,21 +991,11 @@ def collect_windows(info_add):
959991 pass
960992
961993 # windows.version_caption: "wmic os get Caption,Version /value" command
962- import subprocess
963- try :
964- # When wmic.exe output is redirected to a pipe,
965- # it uses the OEM code page
966- proc = subprocess .Popen (["wmic" , "os" , "get" , "Caption,Version" , "/value" ],
967- stdout = subprocess .PIPE ,
968- stderr = subprocess .PIPE ,
969- encoding = "oem" ,
970- text = True )
971- output , stderr = proc .communicate ()
972- if proc .returncode :
973- output = ""
974- except OSError :
975- pass
976- else :
994+ output = run_command (["wmic" , "os" , "get" , "Caption,Version" , "/value" ],
995+ # When wmic.exe output is redirected to a pipe,
996+ # it uses the OEM code page
997+ encoding = "oem" )
998+ if output :
977999 for line in output .splitlines ():
9781000 line = line .strip ()
9791001 if line .startswith ('Caption=' ):
@@ -986,23 +1008,11 @@ def collect_windows(info_add):
9861008 info_add ('windows.version' , line )
9871009
9881010 # windows.ver: "ver" command
989- try :
990- proc = subprocess .Popen (["ver" ], shell = True ,
991- stdout = subprocess .PIPE ,
992- stderr = subprocess .PIPE ,
993- text = True )
994- output = proc .communicate ()[0 ]
995- if proc .returncode == 0xc0000142 :
996- return
997- if proc .returncode :
998- output = ""
999- except OSError :
1000- return
1001- else :
1002- output = output .strip ()
1003- line = output .splitlines ()[0 ]
1004- if line :
1005- info_add ('windows.ver' , line )
1011+ output = run_command (["ver" ], shell = True )
1012+ # "ver" output starts with an empty line: remove it
1013+ output = output .strip ()
1014+ if output :
1015+ info_add ('windows.ver' , first_line (output ))
10061016
10071017 # windows.developer_mode: get AllowDevelopmentWithoutDevLicense registry
10081018 value = winreg_query (r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows"
@@ -1113,7 +1123,45 @@ def get_machine_id():
11131123 return None
11141124
11151125
1116- def collect_linux (info_add ):
1126+ def detect_virt ():
1127+ # Run systemd-detect-virt command
1128+ virt = run_command (["systemd-detect-virt" ], check = False )
1129+ if virt and virt != "none" :
1130+ return virt
1131+
1132+ # Check if the process in running in a container
1133+ import os .path
1134+ if os .path .exists ('/.dockerenv' ):
1135+ return 'docker'
1136+ if os .path .exists ('/run/.containerenv' ):
1137+ return 'podman'
1138+
1139+ container = read_first_line ('/run/systemd/container' )
1140+ if container :
1141+ return container
1142+
1143+ if APPLE :
1144+ hv_vmm_present = run_command (['sysctl' , '-n' , 'kern.hv_vmm_present' ])
1145+ if hv_vmm_present == '1' :
1146+ return 'run in a VM (kern.hv_vmm_present is 1)'
1147+
1148+ # Other ways to check if running in a container:
1149+ # * Parse /proc/1/mounts or /proc/1/mountinfo (check "/" filesystem).
1150+ # * Parse /proc/1/cgroup.
1151+ # * Parse the first line of /proc/1/sched (check process name is different
1152+ # than "init" and "systemd").
1153+ # * Check / inode.
1154+ # * On systems using SELinux (Fedora/CentOS/RHEL), check for "container_t"
1155+ # label, for example of /proc/1/attr/current.
1156+ # * Check for "container" variable in /proc/1/environ
1157+ # (only root can read this file).
1158+ # * Check for "container" environment variable.
1159+ # * Set a specific env var when creating the container image.
1160+ # * Run virt-what, need to install the script, and must be run as root.
1161+ # * Check for "GITHUB_ACTIONS" environmant variable (GitHub Action).
1162+
1163+
1164+ def collect_system (info_add ):
11171165 boot_id = read_first_line ("/proc/sys/kernel/random/boot_id" )
11181166 if boot_id :
11191167 info_add ('system.boot_id' , boot_id )
@@ -1133,6 +1181,15 @@ def collect_linux(info_add):
11331181 uptime = f'{ uptime } sec'
11341182 info_add ('system.uptime' , uptime )
11351183
1184+ virt = detect_virt ()
1185+ if virt :
1186+ info_add ('system.virt' , virt )
1187+
1188+ if APPLE :
1189+ hardware = run_command (['sysctl' , '-n' , 'hw.model' ])
1190+ if hardware :
1191+ info_add ('system.hardware' , hardware )
1192+
11361193
11371194def collect_info (info ):
11381195 error = False
@@ -1174,7 +1231,7 @@ def collect_info(info):
11741231 collect_windows ,
11751232 collect_zlib ,
11761233 collect_libregrtest_utils ,
1177- collect_linux ,
1234+ collect_system ,
11781235
11791236 # Collecting from tests should be last as they have side effects.
11801237 collect_test_socket ,
0 commit comments