1<?php 2 3/* 4 Phoronix Test Suite 5 URLs: http://www.phoronix.com, http://www.phoronix-test-suite.com/ 6 Copyright (C) 2008 - 2021, Phoronix Media 7 Copyright (C) 2008 - 2021, Michael Larabel 8 9 This program is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 3 of the License, or 12 (at your option) any later version. 13 14 This program is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with this program. If not, see <http://www.gnu.org/licenses/>. 21*/ 22 23class pts_client 24{ 25 public static $display = false; 26 public static $pts_logger = false; 27 public static $web_result_viewer_active = false; 28 public static $web_result_viewer_access_key = false; 29 public static $has_used_modern_result_viewer = false; 30 public static $last_browser_launch_time = 0; 31 public static $last_browser_duration = 0; 32 public static $last_result_view_url = null; 33 public static $skip_log_file_type_checks = false; 34 protected static $lock_pointers = null; 35 protected static $phoromatic_servers = array(); 36 protected static $debug_mode = false; 37 protected static $full_output = false; 38 protected static $override_pts_env_vars = array(); 39 protected static $sent_command = null; 40 protected static $time_pts_last_launch = null; 41 private static $current_command = null; 42 private static $forked_pids = array(); 43 private static $download_speed_average_count = -1; 44 private static $download_speed_average_speed = -1; 45 46 public static function create_lock($lock_file) 47 { 48 if(isset(self::$lock_pointers[$lock_file]) || is_writable(dirname($lock_file)) == false) 49 { 50 return false; 51 } 52 else if(disk_free_space(dirname($lock_file)) < 1024) 53 { 54 echo PHP_EOL . 'Lock creation failed due to lack of disk space.' . PHP_EOL; 55 return false; 56 } 57 58 self::$lock_pointers[$lock_file] = fopen($lock_file, 'w'); 59 chmod($lock_file, 0644); 60 return self::$lock_pointers[$lock_file] != false && (phodevi::is_windows() || flock(self::$lock_pointers[$lock_file], LOCK_EX | LOCK_NB)); 61 } 62 public static function current_time() 63 { 64 $current_time = time(); 65 66 // Calculate time based on offset from last OpenBenchmarking.org check and then the time since that file was modified 67 $repo_index = pts_openbenchmarking::read_repository_index('pts'); 68 if($repo_index && isset($repo_index['main']['generated'])) 69 { 70 $repo_time = $repo_index['main']['generated']; 71 if(($index = pts_openbenchmarking::is_repository('pts'))) 72 { 73 $last_modified = filemtime($index); 74 $time_since_modify = time() - $last_modified; 75 } 76 77 $calculated_time = $repo_time + $time_since_modify; 78 if($calculated_time > $current_time) 79 { 80 $current_time = $calculated_time; 81 } 82 } 83 84 // Fallback to checking time since the PTS release to see if date computed is behind that 85 if(($fallback_time = strtotime(PTS_RELEASE_DATE . ' ' . date('H:i:s'))) > $current_time) 86 { 87 $current_time = $fallback_time; 88 } 89 90 return $current_time; 91 } 92 public static function possible_sub_commands() 93 { 94 static $options = null; 95 96 if(empty($options)) 97 { 98 $options = array(); 99 foreach(pts_file_io::glob(PTS_COMMAND_PATH . '*.php') as $option_php) 100 { 101 $name = str_replace('_', '-', basename($option_php, '.php')); 102 if(!in_array(pts_strings::first_in_string($name, '-'), array('dump', 'debug', 'task'))) 103 { 104 $options[] = $name; 105 } 106 } 107 } 108 109 return $options; 110 } 111 public static function is_locked($lock_file) 112 { 113 $fp = fopen($lock_file, 'w'); 114 $is_locked = $fp && !flock($fp, LOCK_EX | LOCK_NB); 115 $fp && fclose($fp); 116 117 return $is_locked; 118 } 119 public static function release_lock($lock_file) 120 { 121 // Remove lock 122 if(isset(self::$lock_pointers[$lock_file]) == false) 123 { 124 pts_file_io::unlink($lock_file); 125 return false; 126 } 127 128 if(is_resource(self::$lock_pointers[$lock_file])) 129 { 130 fclose(self::$lock_pointers[$lock_file]); 131 } 132 133 pts_file_io::unlink($lock_file); 134 unset(self::$lock_pointers[$lock_file]); 135 } 136 public static function write_to_lock($lock_file, $contents) 137 { 138 if(isset(self::$lock_pointers[$lock_file]) && is_resource(self::$lock_pointers[$lock_file])) 139 { 140 fwrite(self::$lock_pointers[$lock_file], $contents); 141 fflush(self::$lock_pointers[$lock_file]); 142 } 143 } 144 public static function init() 145 { 146 pts_core::init(); 147 pts_define('PTS_COMMAND_PATH', PTS_CORE_PATH . 'commands/'); 148 149 if(defined('QUICK_START') && QUICK_START) 150 { 151 return true; 152 } 153 154 155 if(function_exists('cli_set_process_title') && PHP_OS == 'Linux') 156 { 157 cli_set_process_title('Phoronix Test Suite'); 158 } 159 160 pts_define('PHP_BIN', pts_client::read_env('PHP_BIN')); 161 162 $dir_init = array(PTS_USER_PATH); 163 foreach($dir_init as $dir) 164 { 165 pts_file_io::mkdir($dir); 166 } 167 168 if(PTS_IS_CLIENT) 169 { 170 pts_network::client_startup(); 171 } 172 173 self::core_storage_init_process(); 174 $p = pts_config::read_path_config('PhoronixTestSuite/Options/Installation/EnvironmentDirectory', '~/.phoronix-test-suite/installed-tests/'); 175 if(phodevi::is_windows()) 176 { 177 $p = str_replace('/', DIRECTORY_SEPARATOR, $p); 178 } 179 pts_define('PTS_TEST_INSTALL_DEFAULT_PATH', $p); 180 181 pts_define('PTS_SAVE_RESULTS_PATH', pts_config::read_path_config('PhoronixTestSuite/Options/Testing/ResultsDirectory', '~/.phoronix-test-suite/test-results/')); 182 self::extended_init_process(); 183 184 $openbenchmarking = pts_storage_object::read_from_file(PTS_CORE_STORAGE, 'openbenchmarking'); 185 $openbenchmarking_account_settings = pts_storage_object::read_from_file(PTS_CORE_STORAGE, 'openbenchmarking_account_settings'); 186 187 if($openbenchmarking != null) 188 { 189 // OpenBenchmarking.org Account 190 pts_openbenchmarking_client::init_account($openbenchmarking, $openbenchmarking_account_settings); 191 } 192 193 return true; 194 } 195 private static function extended_init_process() 196 { 197 // Extended Initalization Process 198 $directory_check = array( 199 PTS_TEST_INSTALL_DEFAULT_PATH, 200 PTS_SAVE_RESULTS_PATH, 201 pts_module::module_local_path(), 202 pts_module::module_data_path(), 203 PTS_DOWNLOAD_CACHE_PATH, 204 PTS_OPENBENCHMARKING_SCRATCH_PATH, 205 PTS_TEST_PROFILE_PATH, 206 PTS_TEST_SUITE_PATH, 207 PTS_TEST_PROFILE_PATH . 'local/', 208 PTS_TEST_SUITE_PATH . 'local/' 209 ); 210 211 $tp_pts_dir_not = is_dir(PTS_TEST_PROFILE_PATH . 'pts/'); 212 213 foreach($directory_check as $dir) 214 { 215 pts_file_io::mkdir($dir); 216 } 217 218 // Copy files (without overwrite) from internal OB program cache if present, to help those without Internet 219 if(!phodevi::is_windows() && (FIRST_RUN_ON_PTS_UPGRADE || !$tp_pts_dir_not)) 220 { 221 if(PTS_INTERNAL_OB_CACHE && is_dir(PTS_INTERNAL_OB_CACHE . 'test-profiles')) 222 { 223 pts_file_io::copy(PTS_INTERNAL_OB_CACHE . 'test-profiles', PTS_TEST_PROFILE_PATH, true); 224 } 225 if(PTS_INTERNAL_OB_CACHE && is_dir(PTS_INTERNAL_OB_CACHE . 'test-suites')) 226 { 227 pts_file_io::copy(PTS_INTERNAL_OB_CACHE . 'test-suites', PTS_TEST_SUITE_PATH, true); 228 } 229 if(PTS_INTERNAL_OB_CACHE && is_dir(PTS_INTERNAL_OB_CACHE . 'openbenchmarking.org')) 230 { 231 pts_file_io::copy(PTS_INTERNAL_OB_CACHE . 'openbenchmarking.org', PTS_OPENBENCHMARKING_SCRATCH_PATH, true); 232 233 if(!pts_network::internet_support_available()) 234 { 235 // Only overwrite the OB index files if it's newer 236 foreach(pts_file_io::glob(PTS_INTERNAL_OB_CACHE . 'openbenchmarking.org/*.index') as $cache_index_file) 237 { 238 $index_file_name = basename($cache_index_file); 239 if(is_file(PTS_OPENBENCHMARKING_SCRATCH_PATH . $index_file_name)) 240 { 241 $current_version = pts_openbenchmarking::get_generated_time_from_index(PTS_OPENBENCHMARKING_SCRATCH_PATH . $index_file_name); 242 $cached_version = pts_openbenchmarking::get_generated_time_from_index($cache_index_file); 243 if($cached_version > $current_version) 244 { 245 copy($cache_index_file, PTS_OPENBENCHMARKING_SCRATCH_PATH . $index_file_name); 246 } 247 } 248 } 249 } 250 } 251 } 252 253 // pts_compatibility ops here 254 255 pts_client::init_display_mode(); 256 } 257 public static function module_framework_init() 258 { 259 // Process initially called when PTS starts up 260 // Check for modules to auto-load from the configuration file 261 $load_modules = pts_config::read_user_config('PhoronixTestSuite/Options/Modules/AutoLoadModules', null); 262 263 if(!empty($load_modules)) 264 { 265 foreach(pts_strings::comma_explode($load_modules) as $module) 266 { 267 $module_r = pts_strings::trim_explode('=', $module); 268 269 if(count($module_r) == 2) 270 { 271 // Ideally end up hooking this into pts_module::read_variable() rather than using the real env 272 pts_client::pts_set_environment_variable($module_r[0], $module_r[1]); 273 } 274 else 275 { 276 pts_module_manager::attach_module($module); 277 } 278 } 279 } 280 281 // Check for modules to load manually in PTS_MODULES 282 if(($load_modules = pts_client::read_env('PTS_MODULES')) !== false) 283 { 284 foreach(pts_strings::comma_explode($load_modules) as $module) 285 { 286 if(!pts_module_manager::is_module_attached($module)) 287 { 288 pts_module_manager::attach_module($module); 289 } 290 } 291 } 292 293 // Detect modules to load automatically 294 pts_module_manager::detect_modules_to_load(); 295 296 // Clean-up modules list 297 pts_module_manager::clean_module_list(); 298 299 // Reset counter 300 pts_module_manager::set_current_module(null); 301 302 // Load the modules 303 $module_store_list = array(); 304 foreach(pts_module_manager::attached_modules() as $module) 305 { 306 $class_vars = get_class_vars($module); 307 $module_store_vars = isset($class_vars['module_store_vars']) ? $class_vars['module_store_vars'] : null; 308 309 if(is_array($module_store_vars)) 310 { 311 foreach($module_store_vars as $store_var) 312 { 313 if(!in_array($store_var, $module_store_list)) 314 { 315 $module_store_list[] = $store_var; 316 } 317 } 318 } 319 } 320 321 // Should any of the module options be saved to the results? 322 foreach($module_store_list as $var) 323 { 324 $var_value = pts_client::read_env($var); 325 326 if(!empty($var_value)) 327 { 328 pts_module_manager::var_store_add($var, $var_value); 329 } 330 } 331 332 pts_module_manager::module_process('__startup'); 333 pts_define('PTS_STARTUP_TASK_PERFORMED', true); 334 register_shutdown_function(array('pts_module_manager', 'module_process'), '__shutdown'); 335 } 336 public static function environmental_variables() 337 { 338 // The PTS environmental variables passed during the testing process, etc 339 static $env_variables = null; 340 341 if($env_variables == null) 342 { 343 $physical_cores = phodevi::read_property('cpu', 'physical-core-count'); 344 $i = 0; 345 do 346 { 347 $i++; 348 $nearest_cube = pow($i, 3); 349 } 350 while($physical_cores >= pow(($i + 1), 3)); 351 352 $tpc = phodevi::read_property('cpu', 'core-count') / $physical_cores; 353 if($tpc < 1 || !is_integer($tpc)) 354 { 355 $tpc = 1; 356 } 357 358 $env_variables = array( 359 'PTS_VERSION' => PTS_VERSION, 360 'PTS_CODENAME' => PTS_CODENAME, 361 'PTS_DIR' => PTS_PATH, 362 'PTS_LAUNCHER' => getenv('PTS_LAUNCHER'), 363 'PHP_BIN' => PHP_BIN, 364 'NUM_CPU_CORES' => phodevi::read_property('cpu', 'core-count'), 365 'OMP_NUM_THREADS' => phodevi::read_property('cpu', 'core-count'), 366 'NUM_CPU_PHYSICAL_CORES' => $physical_cores, 367 'NUM_CPU_PHYSICAL_CORES_CUBE' => $nearest_cube, 368 'CPU_THREADS_PER_CORE' => $tpc, 369 'NUM_CPU_NODES' => phodevi::read_property('cpu', 'node-count'), 370 'NUM_CPU_JOBS' => (phodevi::read_property('cpu', 'core-count') * 2), 371 'SYS_MEMORY' => phodevi::read_property('memory', 'capacity'), 372 'VIDEO_MEMORY' => phodevi::read_property('gpu', 'memory-capacity'), 373 'VIDEO_WIDTH' => pts_arrays::first_element(phodevi::read_property('gpu', 'screen-resolution')), 374 'VIDEO_HEIGHT' => pts_arrays::last_element(phodevi::read_property('gpu', 'screen-resolution')), 375 'VIDEO_MONITOR_COUNT' => phodevi::read_property('monitor', 'count'), 376 'VIDEO_MONITOR_LAYOUT' => phodevi::read_property('monitor', 'layout'), 377 'VIDEO_MONITOR_SIZES' => phodevi::read_property('monitor', 'modes'), 378 'OPERATING_SYSTEM' => phodevi::read_property('system', 'vendor-identifier'), 379 'OS_VERSION' => phodevi::read_property('system', 'os-version'), 380 'OS_ARCH' => phodevi::read_property('system', 'kernel-architecture'), 381 'OS_TYPE' => phodevi::os_under_test(), 382 'CPU_FAMILY' => str_replace(' ', '', strtolower(phodevi::read_property('cpu', 'core-family-name'))), 383 'THIS_RUN_TIME' => PTS_INIT_TIME, 384 'DEBUG_REAL_HOME' => pts_core::user_home_directory(), 385 'DEBUG_PATH' => pts_client::get_path(), 386 'SYSTEM_TYPE_ID' => phodevi_base::determine_system_type(phodevi::system_hardware(), phodevi::system_software()), 387 'SYSTEM_TYPE' => phodevi_base::system_type_to_string(phodevi_base::determine_system_type(phodevi::system_hardware(), phodevi::system_software())), 388 'TERMINAL_WIDTH' => pts_client::terminal_width(), 389 'C_CXX_FLAGS_DEFAULT' => '-O3 -march=native', // mostly for future use 390 'GPU_DEVICE_ID' => phodevi::read_property('gpu', 'device-id'), 391 //'PATH' => pts_client::get_path() 392 ); 393 394 if(!pts_client::executable_in_path('cc') && getenv('CC') == false) 395 { 396 // This helps some test profiles build correctly if they don't do a cc check internally 397 if(pts_client::executable_in_path('gcc')) 398 { 399 $env_variables['CC'] = 'gcc'; 400 } 401 else if(pts_client::executable_in_path('clang')) 402 { 403 $env_variables['CC'] = 'clang'; 404 } 405 } 406 } 407 408 return array_merge($env_variables, self::$override_pts_env_vars); 409 } 410 public static function override_pts_env_var($name, $value) 411 { 412 self::$override_pts_env_vars[$name] = $value; 413 } 414 public static function unset_pts_env_var_override($name) 415 { 416 if(isset(self::$override_pts_env_vars[$name])) 417 { 418 unset(self::$override_pts_env_vars[$name]); 419 } 420 } 421 public static function test_install_root_path() 422 { 423 if(getenv('PTS_TEST_INSTALL_ROOT_PATH') != false && is_dir(getenv('PTS_TEST_INSTALL_ROOT_PATH')) && is_writable(getenv('PTS_TEST_INSTALL_ROOT_PATH'))) 424 { 425 return getenv('PTS_TEST_INSTALL_ROOT_PATH'); 426 } 427 else 428 { 429 if(!defined('PTS_TEST_INSTALL_DEFAULT_PATH')) 430 { 431 $p = pts_config::read_path_config('PhoronixTestSuite/Options/Installation/EnvironmentDirectory', '~/.phoronix-test-suite/installed-tests/'); 432 if(phodevi::is_windows()) 433 { 434 $p = str_replace('/', DIRECTORY_SEPARATOR, $p); 435 } 436 437 pts_define('PTS_TEST_INSTALL_DEFAULT_PATH', $p); 438 } 439 440 return PTS_TEST_INSTALL_DEFAULT_PATH; 441 } 442 } 443 public static function download_cache_path() 444 { 445 return pts_strings::add_trailing_slash(pts_config::read_path_config('PhoronixTestSuite/Options/Installation/CacheDirectory', PTS_DOWNLOAD_CACHE_PATH)); 446 } 447 public static function supports_colored_text_output() 448 { 449 static $supports = -1; 450 451 if($supports == -1) 452 { 453 if(getenv('NO_COLOR')) 454 { 455 $supported = false; 456 } 457 else 458 { 459 $config_color_option = pts_config::read_user_config('PhoronixTestSuite/Options/General/ColoredConsole', 'AUTO'); 460 461 switch(strtoupper($config_color_option)) 462 { 463 case 'TRUE': 464 $supported = true; 465 break; 466 case 'FALSE': 467 $supported = false; 468 break; 469 case 'AUTO': 470 default: 471 $supported = (function_exists('posix_isatty') && posix_isatty(STDOUT)) || (PTS_IS_CLIENT && (getenv('LS_COLORS') || getenv('CLICOLOR'))) || (phodevi::is_windows() && strstr(phodevi::read_property('system', 'operating-system'), 'Windows 8') === false && strstr(phodevi::read_property('system', 'operating-system'), 'Windows 7') === false); 472 break; 473 } 474 } 475 $supports = $supported; 476 } 477 478 return $supports; 479 } 480 public static function hex_color_to_string($hex) 481 { 482 $colors = array(); 483 list($colors['red'], $colors['green'], $colors['blue']) = sscanf($hex, "#%02x%02x%02x"); 484 if($colors['red'] > 240 && $colors['green'] > 210) 485 { 486 return 'yellow'; // this works for Arm color, etc 487 } 488 return array_search(max($colors), $colors); 489 } 490 public static function cli_colored_text($str, $color, $bold = false) 491 { 492 if(!self::supports_colored_text_output() || empty($color)) 493 { 494 return $str; 495 } 496 497 $attribute = ($bold ? '1' : '0'); 498 $colors = array( 499 'black' => $attribute . ';30', 500 'gray' => '1;30', // gray not bold doesn't look good in all consoles 501 'blue' => $attribute . ';34', 502 'magenta' => $attribute . ';35', 503 'green' => $attribute . ';32', 504 'yellow' => $attribute . ';33', 505 'red' => $attribute . ';31', 506 'cyan' => $attribute . ';36', 507 'white' => $attribute . ';37', 508 ); 509 510 if(!isset($colors[$color])) 511 { 512 return $str; 513 } 514 515 return "\033[" . $colors[$color] . 'm' . $str . "\033[0m"; 516 } 517 public static function cli_just_bold($str) 518 { 519 if(!self::supports_colored_text_output()) 520 { 521 return $str; 522 } 523 524 return "\033[1m$str\033[0m"; 525 } 526 public static function cli_just_italic($str) 527 { 528 if(!self::supports_colored_text_output()) 529 { 530 return $str; 531 } 532 533 return "\e[3m$str\e[0m"; 534 } 535 public static function cli_just_underline($str) 536 { 537 if(!self::supports_colored_text_output()) 538 { 539 return $str; 540 } 541 542 return "\e[4m$str\e[0m"; 543 } 544 public static function save_test_result($save_to = null, $save_results = null, $render_graphs = true, $result_identifier = null) 545 { 546 // Saves PTS result file 547 if(substr($save_to, -4) != '.xml') 548 { 549 $save_to .= '.xml'; 550 } 551 $save_to = str_replace(PTS_SAVE_RESULTS_PATH, '', $save_to); 552 553 $save_to_dir = pts_client::setup_test_result_directory($save_to); 554 555 if($save_to == null || $save_results == null) 556 { 557 $bool = false; 558 } 559 else 560 { 561 $save_name = basename($save_to, '.xml'); 562 563 $bool = file_put_contents(PTS_SAVE_RESULTS_PATH . $save_to, $save_results); 564 565 if($result_identifier != null && pts_config::read_bool_config('PhoronixTestSuite/Options/Testing/SaveSystemLogs', 'TRUE')) 566 { 567 // Save verbose system information here 568 //$system_log_dir = $result_file->get_system_log_dir($result_identifier, false); 569 $system_log_dir = $save_to_dir . '/system-logs/' . pts_strings::simplify_string_for_file_handling($result_identifier) . '/'; 570 pts_file_io::mkdir($system_log_dir, 0777, true); 571 572 // Backup system files 573 // TODO: move out these files/commands to log out to respective Phodevi components so only what's relevant will be logged 574 $system_log_files = array( 575 '/var/log/Xorg.0.log', 576 '/proc/cpuinfo', 577 '/proc/meminfo', 578 '/proc/modules', 579 '/proc/mounts', 580 '/proc/cmdline', 581 '/proc/version', 582 '/proc/mdstat', 583 '/proc/lock_stat', 584 '/etc/X11/xorg.conf', 585 '/sys/kernel/debug/dri/0/radeon_pm_info', 586 '/sys/kernel/debug/dri/0/i915_capabilities', 587 '/sys/kernel/debug/dri/0/i915_cur_delayinfo', 588 '/sys/kernel/debug/dri/0/i915_drpc_info', 589 '/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies', 590 ); 591 592 /* 593 if(phodevi::is_linux()) 594 { 595 // the kernel config file might just be too large to upload for now 596 $system_log_files[] = '/boot/config-' . php_uname('r'); 597 } 598 */ 599 600 foreach($system_log_files as $file) 601 { 602 if(is_file($file) && is_readable($file) && filesize($file) < 1000000) 603 { 604 // copy() can't be used in this case since it will result in a blank file for /proc/ file-system 605 $file_contents = file_get_contents($file); 606 $file_contents = pts_strings::remove_line_timestamps($file_contents); 607 file_put_contents($system_log_dir . basename($file), $file_contents); 608 } 609 } 610 611 $kconfig = null; 612 if(is_file('/proc/config.gz') && pts_client::executable_in_path('zcat')) 613 { 614 $kconfig = shell_exec('zcat /proc/config.gz'); 615 } 616 else if(pts_client::executable_in_path('uname') && ($uname_r = trim(shell_exec('uname -r 2>&1'))) && is_file('/boot/config-' . $uname_r)) 617 { 618 $kconfig = file_get_contents('/boot/config-' . $uname_r); 619 } 620 if($kconfig != null) 621 { 622 $kconfig = phodevi_vfs::cleanse_and_shorten_kernel_config($kconfig); 623 file_put_contents($system_log_dir . 'config', $kconfig); 624 } 625 626 // Generate logs from system commands to backup 627 $system_log_commands = array( 628 'lspci -mmkvvvnn', 629 'lscpu', 630 'cc -v', 631 // 'lsusb', 632 'lsmod', 633 'sensors', 634 'dmesg', 635 'vdpauinfo', 636 'cpufreq-info', 637 'glxinfo', 638 'clinfo', 639 'vulkaninfo', 640 'uname -a', 641 // 'udisks --dump', 642 //'upower --dump', 643 'dmidecode', 644 ); 645 646 if(phodevi::is_linux() && phodevi::read_property('system', 'filesystem') == 'ext4' && phodevi::is_root()) 647 { 648 $system_log_commands[] = 'dumpe2fs -h ' . phodevi::read_property('disk', 'device-providing-storage'); 649 } 650 651 if(phodevi::is_bsd()) 652 { 653 $system_log_commands[] = 'sysctl -a'; 654 $system_log_commands[] = 'kenv'; 655 } 656 657 foreach($system_log_commands as $command_string) 658 { 659 $command = explode(' ', $command_string); 660 661 if(($command_bin = pts_client::executable_in_path($command[0]))) 662 { 663 $cmd_output = shell_exec('cd "' . dirname($command_bin) . '" && ./' . $command_string . ' 2>&1'); 664 665 if(strlen($cmd_output) > 900000) 666 { 667 // Don't preserve really large logs, likely filled with lots of junk 668 $cmd_output = null; 669 continue; 670 } 671 if(strpos($cmd_output, 'read kernel buffer failed: Operation not permitted') !== false || strpos($cmd_output, 'Error: unable to open display') !== false) 672 { 673 continue; 674 } 675 676 // Try to filter out any serial numbers, etc. 677 phodevi_vfs::cleanse_file($cmd_output, $command[0]); 678 $cmd_output = pts_strings::remove_line_timestamps($cmd_output); 679 680 file_put_contents($system_log_dir . $command[0], $cmd_output); 681 } 682 } 683 684 // Dump some common / important environmental variables 685 $environment_variables = array( 686 'PATH' => null, 687 'CFLAGS' => null, 688 'CXXFLAGS' => null, 689 'LD_LIBRARY_PATH' => null, 690 'CC' => null, 691 'CXX' => null, 692 'LIBGL_DRIVERS_PATH' => null 693 ); 694 695 foreach($environment_variables as $variable => &$value) 696 { 697 $v = getenv($variable); 698 699 if($v != null) 700 { 701 $value = $v; 702 } 703 else 704 { 705 unset($environment_variables[$variable]); 706 } 707 } 708 709 if(!empty($environment_variables)) 710 { 711 $variable_dump = null; 712 foreach($environment_variables as $variable => $value) 713 { 714 $variable_dump .= $variable . '=' . $value . PHP_EOL; 715 } 716 file_put_contents($system_log_dir . 'environment-variables', $variable_dump); 717 } 718 719 if(($extra_logs_dir = getenv('PTS_EXTRA_SYSTEM_LOGS_DIR')) != false && is_dir($extra_logs_dir)) 720 { 721 // Allow extra arbitrary system logs to be collected within PTS_EXTRA_SYSTEM_LOGS_DIR 722 foreach(pts_file_io::glob($extra_logs_dir . '/*') as $extra_log) 723 { 724 $extra_log_basename = basename($extra_log); 725 726 // Don't overwrite existing auto-generated system log files + also ensure log file is text and not binary etc payload 727 if(!is_file($system_log_dir . $extra_log_basename) && (self::$skip_log_file_type_checks || pts_file_io::is_text_file($extra_log))) 728 { 729 copy($extra_log, $system_log_dir . $extra_log_basename); 730 } 731 } 732 } 733 734 pts_module_manager::module_process('__post_test_run_system_logs', $system_log_dir); 735 } 736 } 737 738 return $bool; 739 } 740 public static function init_display_mode($override_display_mode = false) 741 { 742 if(PTS_IS_WEB_CLIENT && !defined('PHOROMATIC_SERVER')) 743 { 744 self::$display = new pts_web_display_mode(); 745 return; 746 } 747 748 $env_mode = pts_client::is_debug_mode() ? 'BASIC' : $override_display_mode; 749 750 switch(($env_mode != false || ($env_mode = pts_client::read_env('PTS_DISPLAY_MODE')) != false ? $env_mode : pts_config::read_user_config('PhoronixTestSuite/Options/General/DefaultDisplayMode', 'DEFAULT'))) 751 { 752 case 'BASIC': 753 self::$display = new pts_basic_display_mode(); 754 break; 755 case 'BATCH': 756 case 'CONCISE': 757 self::$display = new pts_concise_display_mode(); 758 break; 759 case 'SHORT': 760 self::$display = new pts_short_display_mode(); 761 break; 762 case 'DEFAULT': 763 default: 764 self::$display = new pts_concise_display_mode(); 765 break; 766 } 767 } 768 public static function program_requirement_checks($only_show_required = false, $always_report = false) 769 { 770 $extension_checks = pts_needed_extensions(); 771 772 $printed_required_header = false; 773 $printed_optional_header = false; 774 foreach($extension_checks as $extension) 775 { 776 if($extension[1] == false || $always_report) 777 { 778 if($always_report) 779 { 780 $printed_required_header = true; 781 $printed_optional_header = true; 782 echo ($extension[1] == false ? 'MISSING' : 'PRESENT') . ' - '; 783 } 784 785 if($extension[0] == 1) 786 { 787 // Oops, this extension is required 788 if($printed_required_header == false) 789 { 790 echo PHP_EOL . 'The following PHP extensions are REQUIRED:' . PHP_EOL . PHP_EOL; 791 $printed_required_header = true; 792 } 793 } 794 else 795 { 796 if(($only_show_required || PTS_IS_DAEMONIZED_SERVER_PROCESS) && $printed_required_header == false) 797 { 798 continue; 799 } 800 801 // This extension is missing but optional 802 if($printed_optional_header == false) 803 { 804 echo PHP_EOL . ($printed_required_header ? null : 'NOTICE: ') . 'The following PHP extensions are OPTIONAL but recommended:' . PHP_EOL . PHP_EOL; 805 $printed_optional_header = true; 806 } 807 } 808 809 echo sprintf('%-9ls %-30ls' . PHP_EOL, $extension[2], $extension[3]); 810 } 811 } 812 813 if($printed_required_header || $printed_optional_header) 814 { 815 echo PHP_EOL; 816 817 if($printed_required_header && !$always_report) 818 { 819 exit; 820 } 821 } 822 } 823 private static function core_storage_init_process() 824 { 825 $pso = pts_storage_object::recover_from_file(PTS_CORE_STORAGE); 826 827 if($pso == false) 828 { 829 $pso = new pts_storage_object(true, true); 830 } 831 832 // OpenBenchmarking.org - GSID 833 $global_gsid = $pso->read_object('global_system_id'); 834 $global_gsid_e = $pso->read_object('global_system_id_e'); 835 $global_gsid_p = $pso->read_object('global_system_id_p'); 836 837 if(empty($global_gsid) || pts_openbenchmarking::is_valid_gsid_format($global_gsid) == false) 838 { 839 // Global System ID for anonymous uploads, etc 840 $requested_gsid = true; 841 $global_gsid = pts_openbenchmarking_client::request_gsid(); 842 843 if(is_array($global_gsid)) 844 { 845 $pso->add_object('global_system_id', $global_gsid['gsid']); // GSID 846 $pso->add_object('global_system_id_p', $global_gsid['gsid_p']); // GSID_P 847 $pso->add_object('global_system_id_e', $global_gsid['gsid_e']); // GSID_E 848 pts_define('PTS_GSID', $global_gsid['gsid']); 849 pts_define('PTS_GSID_E', $global_gsid['gsid_e']); 850 } 851 } 852 else if(pts_openbenchmarking::is_valid_gsid_e_format($global_gsid_e) == false || pts_openbenchmarking::is_valid_gsid_p_format($global_gsid_p) == false) 853 { 854 pts_define('PTS_GSID', $global_gsid); 855 $requested_gsid = false; 856 $global_gsid = pts_openbenchmarking_client::retrieve_gsid(); 857 858 if(is_array($global_gsid)) 859 { 860 $pso->add_object('global_system_id_p', $global_gsid['gsid_p']); // GSID_P 861 $pso->add_object('global_system_id_e', $global_gsid['gsid_e']); // GSID_E 862 pts_define('PTS_GSID_E', $global_gsid['gsid_e']); 863 } 864 } 865 else 866 { 867 pts_define('PTS_GSID', $global_gsid); 868 pts_define('PTS_GSID_E', $global_gsid_e); 869 $requested_gsid = false; 870 } 871 872 $machine_self_id = $pso->read_object('machine_self_id'); 873 if(empty($machine_self_id)) 874 { 875 $ns = md5('phoronix-test-suite'); 876 $binary_ns = null; 877 878 for($i = 0; $i < strlen($ns); $i += 2) 879 { 880 $binary_ns .= chr(hexdec($ns[$i] . $ns[$i + 1])); 881 } 882 883 $msi_hash = sha1($binary_ns . uniqid(PTS_CORE_VERSION, true) . getenv('USERNAME') . getenv('USER') . getenv('HOSTNAME') . phodevi::read_property('network', 'ip')); 884 885 $machine_self_id = sprintf('%08s-%04s-%04x-%04x-%12s', substr($msi_hash, 0, 8), substr($msi_hash, 8, 4), (hexdec(substr($msi_hash, 12, 4)) & 0x0fff) | 0x5000, (hexdec(substr($msi_hash, 16, 4)) & 0x3fff) | 0x8000, substr($msi_hash, 20, 12)); 886 // machine_self_id is self-generated unique name for Phoromatic/OB purposes in UUIDv5 format 887 $pso->add_object('machine_self_id', $machine_self_id); 888 } 889 pts_define('PTS_MACHINE_SELF_ID', $machine_self_id); 890 891 // Last Run Processing 892 $last_core_version = $pso->read_object('last_core_version'); 893 pts_define('FIRST_RUN_ON_PTS_UPGRADE', ($last_core_version != PTS_CORE_VERSION)); 894 895 if(FIRST_RUN_ON_PTS_UPGRADE || ($pso->read_object('last_php_version') != PTS_PHP_VERSION)) 896 { 897 // Report any missing/recommended extensions 898 self::program_requirement_checks(); 899 } 900 901 if(FIRST_RUN_ON_PTS_UPGRADE) 902 { 903 if($requested_gsid == false && pts_network::internet_support_available()) 904 { 905 pts_openbenchmarking_client::update_gsid(); 906 } 907 908 $pso->add_object('environmental_variables_for_modules', pts_module_manager::modules_environmental_variables()); 909 $pso->add_object('command_alias_list', pts_documentation::client_commands_aliases()); 910 } 911 $pso->add_object('last_core_version', PTS_CORE_VERSION); // PTS version last run 912 $pso->add_object('last_php_version', PTS_PHP_VERSION); // PHP version last run 913 914 //$last_pts_version = $pso->read_object('last_pts_version'); 915 // do something here with $last_pts_version if you want that information 916 $pso->add_object('last_pts_version', PTS_VERSION); // PTS version last run 917 918 // Last Run Processing 919 $last_run = $pso->read_object('last_run_time'); 920 pts_define('TIME_PTS_LAUNCHED', time()); 921 pts_define('IS_FIRST_RUN_TODAY', (substr($last_run, 0, 10) != date('Y-m-d', TIME_PTS_LAUNCHED))); 922 $pso->add_object('last_run_time', date('Y-m-d H:i:s', TIME_PTS_LAUNCHED)); // Time PTS was last run 923 self::$time_pts_last_launch = strtotime($last_run); 924 pts_define('TIME_SINCE_LAST_RUN', ceil((TIME_PTS_LAUNCHED - self::$time_pts_last_launch) / 60)); // TIME_SINCE_LAST_RUN is in minutes 925 926 // User Agreement Checking 927 $agreement_cs = $pso->read_object('user_agreement_cs'); 928 929 $pso->add_object('user_agreement_cs', $agreement_cs); // User agreement check-sum 930 931 // Phodevi Cache Handling 932 $phodevi_cache = $pso->read_object('phodevi_smart_cache'); 933 934 if($phodevi_cache instanceof phodevi_cache && pts_client::read_env('NO_PHODEVI_CACHE') == false) 935 { 936 $phodevi_cache = $phodevi_cache->restore_cache(PTS_USER_PATH, PTS_CORE_VERSION); 937 phodevi::set_device_cache($phodevi_cache); 938 939 if(($external_phodevi_cache = pts_client::read_env('EXTERNAL_PHODEVI_CACHE'))) 940 { 941 if(is_dir($external_phodevi_cache) && is_file($external_phodevi_cache . '/core.pt2so')) 942 { 943 $external_phodevi_cache .= '/core.pt2so'; 944 } 945 946 if(is_file($external_phodevi_cache)) 947 { 948 $external_phodevi_cache = pts_storage_object::force_recover_from_file($external_phodevi_cache); 949 950 if($external_phodevi_cache != false) 951 { 952 $external_phodevi_cache = $external_phodevi_cache->read_object('phodevi_smart_cache'); 953 $external_phodevi_cache = $external_phodevi_cache->restore_cache(null, PTS_CORE_VERSION); 954 955 if($external_phodevi_cache != false) 956 { 957 //unset($external_phodevi_cache['system']['operating-system']); 958 //unset($external_phodevi_cache['system']['vendor-identifier']); 959 phodevi::set_device_cache($external_phodevi_cache); 960 } 961 } 962 } 963 } 964 } 965 966 // Archive to disk 967 $pso->save_to_file(PTS_CORE_STORAGE); 968 } 969 public static function get_time_pts_last_started() 970 { 971 return self::$time_pts_last_launch; 972 } 973 public static function register_phoromatic_server($server_ip, $http_port) 974 { 975 self::$phoromatic_servers[] = array('ip' => $server_ip, 'http_port' => $http_port); 976 } 977 public static function available_phoromatic_servers() 978 { 979 if(defined('PHOROMATIC_SERVER') && PHOROMATIC_SERVER) 980 { 981 return array(); 982 } 983 984 static $last_phoromatic_scan = 0; 985 static $phoromatic_servers = false; 986 987 // Cache the Phoromatic Server information for 2 minutes since could be expensive to compute 988 if($phoromatic_servers === false || $last_phoromatic_scan < (time() - 120)) 989 { 990 $last_phoromatic_scan = time(); 991 $phoromatic_servers = array(); 992 $possible_servers = pts_network::find_zeroconf_phoromatic_servers(true); 993 994 foreach(self::$phoromatic_servers as $server) 995 { 996 if(phodevi::read_property('network', 'ip') != $server['ip']) 997 { 998 $possible_servers[] = array($server['ip'], $server['http_port']); 999 } 1000 } 1001 1002 $user_config_phoromatic_servers = pts_config::read_user_config('PhoronixTestSuite/Options/General/PhoromaticServers', ''); 1003 foreach(explode(',', $user_config_phoromatic_servers) as $static_server) 1004 { 1005 $static_server = explode(':', $static_server); 1006 if(count($static_server) == 2) 1007 { 1008 $possible_servers[] = array($static_server[0], $static_server[1]); 1009 } 1010 } 1011 1012 if(is_file(PTS_USER_PATH . 'phoromatic-servers')) 1013 { 1014 $phoromatic_servers_file = pts_file_io::file_get_contents(PTS_USER_PATH . 'phoromatic-servers'); 1015 foreach(explode(PHP_EOL, $phoromatic_servers_file) as $ps_file_line) 1016 { 1017 $ps_file_line = explode(':', trim($ps_file_line)); 1018 if(count($ps_file_line) == 2 && ip2long($ps_file_line[0]) !== false && is_numeric($ps_file_line) && $ps_file_line > 100) 1019 { 1020 $possible_servers[] = array($ps_file_line[0], $ps_file_line[1]); 1021 } 1022 } 1023 } 1024 1025 // OpenBenchmarking.org relay zero conf 1026 if(pts_network::internet_support_available()) 1027 { 1028 $ob_relay = pts_openbenchmarking::possible_phoromatic_servers(); 1029 foreach($ob_relay as $s) 1030 { 1031 $local_ip = phodevi::read_property('network', 'ip'); 1032 $local_ip_segments = explode('.', $local_ip); 1033 $s_segments = explode('.', $s[0]); 1034 1035 if($s_segments[0] == $local_ip_segments[0] && $s_segments[1] == $local_ip_segments[1]) 1036 { 1037 $possible_servers[] = array($s[0], $s[1]); 1038 } 1039 } 1040 } 1041 1042 foreach($possible_servers as $possible_server) 1043 { 1044 // possible_server[0] is the Phoromatic Server IP 1045 // possible_server[1] is the Phoromatic Server HTTP PORT 1046 1047 if(in_array($possible_server[0], array_keys($phoromatic_servers)) || phodevi::read_property('network', 'ip') == $possible_server[0]) 1048 { 1049 continue; 1050 } 1051 1052 $server_response = pts_network::http_get_contents('http://' . $possible_server[0] . ':' . $possible_server[1] . '/server.php', false, false, 3); 1053 if(stripos($server_response, 'Phoromatic') !== false) 1054 { 1055 trigger_error('Phoromatic Server Auto-Detected At: ' . $possible_server[0] . ':' . $possible_server[1], E_USER_NOTICE); 1056 $phoromatic_servers[$possible_server[0]] = array('ip' => $possible_server[0], 'http_port' => $possible_server[1]); 1057 } 1058 1059 } 1060 } 1061 1062 return $phoromatic_servers; 1063 } 1064 public static function user_agreement_check($command) 1065 { 1066 $pso = pts_storage_object::recover_from_file(PTS_CORE_STORAGE); 1067 1068 if($pso == false) 1069 { 1070 return false; 1071 } 1072 1073 $config_md5 = $pso->read_object('user_agreement_cs'); 1074 $current_md5 = md5_file(PTS_PATH . 'pts-core/user-agreement.txt'); 1075 1076 if(($config_md5 != $current_md5 || pts_config::read_user_config('PhoronixTestSuite/Options/OpenBenchmarking/AnonymousUsageReporting', 'UNKNOWN') == 'UNKNOWN') && !PTS_IS_DAEMONIZED_SERVER_PROCESS && getenv('PTS_SILENT_MODE') != 1 && $config_md5 != 'enterprise-agree') 1077 { 1078 $prompt_in_method = pts_client::check_command_for_function($command, 'pts_user_agreement_prompt'); 1079 $user_agreement = file_get_contents(PTS_PATH . 'pts-core/user-agreement.txt'); 1080 1081 if($prompt_in_method) 1082 { 1083 $user_agreement_return = call_user_func(array($command, 'pts_user_agreement_prompt'), $user_agreement); 1084 1085 if(is_array($user_agreement_return)) 1086 { 1087 if(count($user_agreement_return) == 3) 1088 { 1089 list($agree, $usage_reporting) = $user_agreement_return; 1090 } 1091 else 1092 { 1093 $agree = array_shift($user_agreement_return); 1094 $usage_reporting = -1; 1095 } 1096 } 1097 else 1098 { 1099 $agree = $user_agreement_return; 1100 $usage_reporting = -1; 1101 } 1102 } 1103 1104 if($prompt_in_method == false || $usage_reporting == -1) 1105 { 1106 pts_client::$display->generic_heading('User Agreement'); 1107 echo wordwrap($user_agreement, (pts_client::terminal_width() - 2)); 1108 $agree = pts_user_io::prompt_bool_input('Do you agree to these terms and wish to proceed', -1); 1109 1110 if(!pts_openbenchmarking::ob_upload_support_available()) 1111 { 1112 $usage_reporting = false; 1113 } 1114 else 1115 { 1116 $usage_reporting = $agree ? pts_user_io::prompt_bool_input('Enable anonymous usage / statistics reporting', -1) : -1; 1117 } 1118 } 1119 1120 if($agree) 1121 { 1122 echo PHP_EOL; 1123 $pso->add_object('user_agreement_cs', $current_md5); 1124 $pso->save_to_file(PTS_CORE_STORAGE); 1125 } 1126 else 1127 { 1128 pts_client::exit_client('In order to run the Phoronix Test Suite, you must agree to the listed terms.'); 1129 } 1130 1131 pts_config::user_config_generate(array( 1132 'PhoronixTestSuite/Options/OpenBenchmarking/AnonymousUsageReporting' => pts_config::bool_to_string($usage_reporting))); 1133 } 1134 1135 if(PTS_IS_CLIENT && getenv('PTS_SILENT_MODE') != 1) 1136 { 1137 pts_external_dependencies::startup_handler(); 1138 } 1139 } 1140 public static function swap_variables($user_str, $replace_call) 1141 { 1142 if(is_array($replace_call)) 1143 { 1144 if(count($replace_call) != 2 || method_exists($replace_call[0], $replace_call[1]) == false) 1145 { 1146 echo PHP_EOL . 'Var Swap With Method Failed.' . PHP_EOL; 1147 return $user_str; 1148 } 1149 } 1150 else if(!function_exists($replace_call)) 1151 { 1152 echo PHP_EOL . 'Var Swap With Function Failed.' . PHP_EOL; 1153 return $user_str; 1154 } 1155 1156 $offset = 0; 1157 $replace_call_return = false; 1158 1159 while($offset < strlen($user_str) && ($s = strpos($user_str, '$', $offset)) !== false) 1160 { 1161 $s++; 1162 $var_name = substr($user_str, $s, (($e = strpos($user_str, ' ', $s)) == false ? strlen($user_str) : $e) - $s); 1163 1164 if($replace_call_return === false) 1165 { 1166 $replace_call_return = call_user_func($replace_call); 1167 } 1168 1169 $var_replacement = isset($replace_call_return[$var_name]) ? $replace_call_return[$var_name] : null; 1170 1171 if($var_replacement != null) 1172 { 1173 $user_str = str_replace('$' . $var_name, $var_replacement, $user_str); 1174 } 1175 else 1176 { 1177 // echo "\nVariable Swap For $var_name Failed.\n"; 1178 } 1179 1180 $offset = $s + strlen($var_replacement); 1181 } 1182 1183 return $user_str; 1184 } 1185 public static function setup_test_result_directory($save_to) 1186 { 1187 $save_to_dir = PTS_SAVE_RESULTS_PATH . $save_to; 1188 1189 if(strpos(basename($save_to_dir), '.')) 1190 { 1191 $save_to_dir = dirname($save_to_dir); 1192 } 1193 1194 if($save_to_dir != '.') 1195 { 1196 pts_file_io::mkdir($save_to_dir); 1197 } 1198 1199 return $save_to_dir; 1200 } 1201 public static function remove_installed_test(&$test_profile) 1202 { 1203 pts_file_io::delete($test_profile->get_install_dir(), null, true); 1204 } 1205 public static function exit_client($string = null, $exit_status = 0) 1206 { 1207 // Exit the Phoronix Test Suite client 1208 pts_define('PTS_EXIT', 1); 1209 1210 if($string != null) 1211 { 1212 echo PHP_EOL . $string . PHP_EOL; 1213 } 1214 1215 exit($exit_status); 1216 } 1217 public static function current_user() 1218 { 1219 // Current system user 1220 return ($pts_user = pts_openbenchmarking_client::user_name()) != null ? $pts_user : phodevi::read_property('system', 'username'); 1221 } 1222 public static function generate_result_file_graphs($test_results_identifier, $save_to_dir = false, $extra_attributes = null) 1223 { 1224 // Since dropping the old result viewer, this function is no longer used except for niche cases (debug render, PDF generation) 1225 1226 if($save_to_dir) 1227 { 1228 if(pts_file_io::mkdir($save_to_dir . '/result-graphs') == false) 1229 { 1230 // Don't delete old files now, in case any modules (e.g. FlameGrapher) output something in there ahead of time 1231 /*// Directory must exist, so remove any old graph files first 1232 foreach(pts_file_io::glob($save_to_dir . '/result-graphs/*') as $old_file) 1233 { 1234 unlink($old_file); 1235 }*/ 1236 } 1237 } 1238 1239 if($test_results_identifier instanceof pts_result_file) 1240 { 1241 $result_file = &$test_results_identifier; 1242 } 1243 else 1244 { 1245 $result_file = new pts_result_file($test_results_identifier); 1246 } 1247 1248 $result_file->avoid_duplicate_identifiers(); 1249 1250 $generated_graphs = array(); 1251 $generated_graph_tables = false; 1252 1253 // Render overview chart 1254 if($save_to_dir) 1255 { 1256 $chart = new pts_ResultFileTable($result_file); 1257 $chart->renderChart($save_to_dir . '/result-graphs/overview.BILDE_EXTENSION'); 1258 1259 $intent = -1; 1260 if(($intent = pts_result_file_analyzer::analyze_result_file_intent($result_file, $intent, true)) || $result_file->get_system_count() == 1) 1261 { 1262 $chart = new pts_ResultFileCompactSystemsTable($result_file, $intent); 1263 } 1264 else 1265 { 1266 $chart = new pts_ResultFileSystemsTable($result_file); 1267 } 1268 $chart->renderChart($save_to_dir . '/result-graphs/systems.BILDE_EXTENSION'); 1269 unset($chart); 1270 1271 if($intent && is_dir($result_file->get_system_log_dir())) 1272 { 1273 $chart = new pts_DetailedSystemComponentTable($result_file, $result_file->get_system_log_dir(), $intent); 1274 1275 if($chart) 1276 { 1277 $chart->renderChart($save_to_dir . '/result-graphs/detailed_component.BILDE_EXTENSION'); 1278 } 1279 } 1280 } 1281 $result_objects = $result_file->get_result_objects(); 1282 $test_titles = array(); 1283 foreach($result_objects as &$result_object) 1284 { 1285 $test_titles[] = $result_object->test_profile->get_title(); 1286 } 1287 1288 $offset = 0; 1289 foreach($result_objects as $key => &$result_object) 1290 { 1291 $save_to = $save_to_dir; 1292 $offset++; 1293 1294 if($save_to_dir && is_dir($save_to_dir)) 1295 { 1296 $save_to .= '/result-graphs/' . $offset . '.BILDE_EXTENSION'; 1297 1298 if(PTS_IS_CLIENT) 1299 { 1300 if($result_file->is_multi_way_comparison(null, $extra_attributes) || pts_client::read_env('GRAPH_GROUP_SIMILAR')) 1301 { 1302 $table_keys = array(); 1303 1304 foreach($test_titles as $this_title_index => $this_title) 1305 { 1306 if(isset($test_titles[$key]) && $this_title == $test_titles[$key]) 1307 { 1308 $table_keys[] = $this_title_index; 1309 } 1310 } 1311 } 1312 else 1313 { 1314 $table_keys = $key; 1315 } 1316 1317 $chart = new pts_ResultFileTable($result_file, null, $table_keys); 1318 $chart->renderChart($save_to_dir . '/result-graphs/' . $offset . '_table.BILDE_EXTENSION'); 1319 unset($chart); 1320 $generated_graph_tables = true; 1321 } 1322 } 1323 1324 $graph = pts_render::render_graph($result_object, $result_file, $save_to, $extra_attributes); 1325 1326 if($graph == false) 1327 { 1328 continue; 1329 } 1330 1331 $generated_graphs[] = $graph; 1332 } 1333 1334 // Generate mini / overview graphs 1335 if($save_to_dir) 1336 { 1337 $graph = new pts_OverviewGraph($result_file); 1338 $rendered = $graph->renderGraph(); 1339 1340 // Check to see if skip_graph was realized during the rendering process 1341 if($rendered) 1342 { 1343 $graph->svg_dom->output($save_to_dir . '/result-graphs/visualize.BILDE_EXTENSION'); 1344 } 1345 unset($graph); 1346 1347 if($result_file->get_system_count() == 2) 1348 { 1349 $graph = new pts_graph_run_vs_run($result_file); 1350 } 1351 else 1352 { 1353 $graph = new pts_graph_radar_chart($result_file); 1354 } 1355 1356 $rendered = $graph->renderGraph(); 1357 1358 // Check to see if skip_graph was realized during the rendering process 1359 if($rendered) 1360 { 1361 $graph->svg_dom->output($save_to_dir . '/result-graphs/radar.BILDE_EXTENSION'); 1362 } 1363 unset($graph); 1364 } 1365 1366 return $generated_graphs; 1367 } 1368 1369 public static function process_shutdown_tasks() 1370 { 1371 // TODO: possibly do something like posix_getpid() != pts_client::$startup_pid in case shutdown function is called from a child process 1372 // Generate Phodevi Smart Cache 1373 if(pts_client::read_env('NO_PHODEVI_CACHE') == false && pts_client::read_env('EXTERNAL_PHODEVI_CACHE') == false) 1374 { 1375 if(pts_config::read_bool_config('PhoronixTestSuite/Options/General/UsePhodeviCache', 'TRUE')) 1376 { 1377 pts_storage_object::set_in_file(PTS_CORE_STORAGE, 'phodevi_smart_cache', phodevi::get_phodevi_cache_object(PTS_USER_PATH, PTS_CORE_VERSION)); 1378 } 1379 else 1380 { 1381 pts_storage_object::set_in_file(PTS_CORE_STORAGE, 'phodevi_smart_cache', null); 1382 } 1383 } 1384 1385 if(is_array(self::$lock_pointers)) 1386 { 1387 foreach(array_keys(self::$lock_pointers) as $lock_file) 1388 { 1389 self::release_lock($lock_file); 1390 } 1391 } 1392 1393 foreach(self::$forked_pids as $pid) 1394 { 1395 if(is_dir('/proc/' . $pid) && function_exists('posix_kill')) 1396 { 1397 posix_kill($pid, SIGKILL); 1398 } 1399 } 1400 } 1401 public static function kill_process_with_children_processes($pid) 1402 { 1403 if(is_dir('/proc/' . $pid) && is_file('/proc/' . $pid . '/task/' . $pid . '/children')) 1404 { 1405 $child_processes = pts_strings::trim_explode(' ', file_get_contents('/proc/' . $pid . '/task/' . $pid . '/children')); 1406 1407 foreach($child_processes as $p) 1408 { 1409 if(!empty($p) && is_dir('/proc/' . $p)) 1410 { 1411 self::kill_process_with_children_processes($p); 1412 } 1413 } 1414 } 1415 if(!empty($pid) && is_dir('/proc/' . $pid)) 1416 { 1417 if(function_exists('posix_kill')) 1418 { 1419 posix_kill($pid, SIGKILL); 1420 } 1421 else 1422 { 1423 shell_exec('kill -9 ' . $pid); 1424 } 1425 sleep(1); 1426 } 1427 } 1428 public static function do_anonymous_usage_reporting() 1429 { 1430 return pts_config::read_bool_config('PhoronixTestSuite/Options/OpenBenchmarking/AnonymousUsageReporting', 0); 1431 } 1432 public static function check_command_for_function($option, $check_function) 1433 { 1434 $in_option = false; 1435 1436 if(is_file(PTS_COMMAND_PATH . $option . '.php')) 1437 { 1438 if(!class_exists($option, false) && is_file(PTS_COMMAND_PATH . $option . '.php')) 1439 { 1440 include(PTS_COMMAND_PATH . $option . '.php'); 1441 } 1442 1443 if(method_exists($option, $check_function)) 1444 { 1445 $in_option = true; 1446 } 1447 } 1448 1449 return $in_option; 1450 } 1451 public static function execute_command($command, $pass_args = null) 1452 { 1453 if(!class_exists($command, false) && is_file(PTS_COMMAND_PATH . $command . '.php')) 1454 { 1455 include(PTS_COMMAND_PATH . $command . '.php'); 1456 } 1457 1458 if(is_file(PTS_COMMAND_PATH . $command . '.php') && method_exists($command, 'argument_checks')) 1459 { 1460 $argument_checks = call_user_func(array($command, 'argument_checks')); 1461 1462 foreach($argument_checks as &$argument_check) 1463 { 1464 $function_check = $argument_check->get_function_check(); 1465 $method_check = false; 1466 1467 if(is_array($function_check) && count($function_check) == 2) 1468 { 1469 $method_check = $function_check[0]; 1470 $function_check = $function_check[1]; 1471 } 1472 1473 if(substr($function_check, 0, 1) == '!') 1474 { 1475 $function_check = substr($function_check, 1); 1476 $return_fails_on = true; 1477 } 1478 else 1479 { 1480 $return_fails_on = false; 1481 } 1482 1483 1484 if($method_check != false) 1485 { 1486 if(!method_exists($method_check, $function_check)) 1487 { 1488 echo PHP_EOL . 'Method check fails.' . PHP_EOL; 1489 continue; 1490 } 1491 1492 $function_check = array($method_check, $function_check); 1493 } 1494 else if(!function_exists($function_check)) 1495 { 1496 continue; 1497 } 1498 1499 // VARIABLE_LENGTH_MAYBE when handling is optional or VARIABLE_LENGTH 1500 if(($maybe = ($argument_check->get_argument_index() === 'VARIABLE_LENGTH_MAYBE')) || $argument_check->get_argument_index() == 'VARIABLE_LENGTH') 1501 { 1502 $return_value = $maybe ? true : null; 1503 1504 foreach($pass_args as $arg) 1505 { 1506 $return_value = call_user_func_array($function_check, array($arg)); 1507 1508 if($return_value == true) 1509 { 1510 break; 1511 } 1512 } 1513 1514 } 1515 else 1516 { 1517 $return_value = call_user_func_array($function_check, array((isset($pass_args[$argument_check->get_argument_index()]) ? $pass_args[$argument_check->get_argument_index()] : null))); 1518 } 1519 1520 if($return_value == $return_fails_on) 1521 { 1522 $command_alias = defined($command . '::doc_use_alias') ? constant($command . '::doc_use_alias') : $command; 1523 1524 if((isset($pass_args[$argument_check->get_argument_index()]) && !empty($pass_args[$argument_check->get_argument_index()])) || ($argument_check->get_argument_index() == 'VARIABLE_LENGTH' && !empty($pass_args))) 1525 { 1526 trigger_error('Invalid Argument: ' . implode(' ', $pass_args), E_USER_ERROR); 1527 } 1528 else 1529 { 1530 trigger_error('Phoronix Test Suite Argument Missing', E_USER_ERROR); 1531 } 1532 1533 echo PHP_EOL . pts_client::cli_just_bold('CORRECT SYNTAX:') . PHP_EOL . 'phoronix-test-suite ' . str_replace('_', '-', $command_alias) . ' ' . pts_client::cli_just_italic(implode(' ', $argument_checks)) . PHP_EOL . PHP_EOL; 1534 pts_client::invalid_command_helper($pass_args, $argument_checks); 1535 1536 return false; 1537 } 1538 else 1539 { 1540 if($argument_check->get_function_return_key() != null && !isset($pass_args[$argument_check->get_function_return_key()])) 1541 { 1542 $pass_args[$argument_check->get_function_return_key()] = $return_value; 1543 } 1544 } 1545 } 1546 } 1547 1548 pts_module_manager::module_process('__pre_option_process', $command); 1549 1550 if(is_file(PTS_COMMAND_PATH . $command . '.php')) 1551 { 1552 self::$current_command = $command; 1553 1554 if(method_exists($command, 'run')) 1555 { 1556 call_user_func(array($command, 'run'), $pass_args); 1557 } 1558 else 1559 { 1560 echo PHP_EOL . 'There is an error in the requested command: ' . $command . PHP_EOL . PHP_EOL; 1561 } 1562 } 1563 else if(($t = pts_module::valid_run_command($command)) != false) 1564 { 1565 list($module, $module_command) = $t; 1566 pts_module_manager::set_current_module($module); 1567 pts_module_manager::run_command($module, $module_command, $pass_args); 1568 pts_module_manager::set_current_module(null); 1569 } 1570 echo PHP_EOL; 1571 1572 pts_module_manager::module_process('__post_option_process', $command); 1573 } 1574 public static function invalid_command_helper($passed_args, &$argument_checks) 1575 { 1576 $supports_passing_a_test = false; 1577 foreach($argument_checks as $check) 1578 { 1579 if($check->get_function_check_type() == 'Test' || strpos($check->get_function_check_type(), 'Test |') !== false) 1580 { 1581 $supports_passing_a_test = true; 1582 } 1583 } 1584 1585 $showed_recent_results = pts_tests::recently_saved_results(); 1586 1587 if($supports_passing_a_test) 1588 { 1589 $tests_to_show = array_keys(pts_openbenchmarking_client::new_and_recently_updated_tests(30, 31, true)); 1590 $tests_to_show_title = 'New Tests'; 1591 1592 if(count($tests_to_show) < 3) 1593 { 1594 $tests_to_show = array_keys(pts_openbenchmarking_client::new_and_recently_updated_tests(60, 31)); 1595 $tests_to_show_title = 'New + Updated Tests'; 1596 } 1597 1598 if(count($tests_to_show) < 3) 1599 { 1600 $tests_to_show = array_keys(pts_openbenchmarking_client::most_popular_tests(20)); 1601 $tests_to_show_title = 'Popular Tests'; 1602 } 1603 1604 if(count($tests_to_show) > 3) 1605 { 1606 $longest_test = strlen(pts_strings::find_longest_string($tests_to_show)) + 3; 1607 $terminal_width = pts_client::terminal_width(); 1608 $tests_per_line = floor($terminal_width / $longest_test); 1609 shuffle($tests_to_show); 1610 $tests_to_show = array_slice($tests_to_show, 0, min(count($tests_to_show), $tests_per_line * 3 - 1)); 1611 1612 echo pts_client::cli_just_bold($tests_to_show_title . ':') . PHP_EOL; 1613 $i = 0; 1614 foreach($tests_to_show as $test) 1615 { 1616 if($i % $tests_per_line == 0) 1617 { 1618 echo ' '; 1619 } 1620 echo $test; 1621 1622 $i++; 1623 if($i % $tests_per_line == 0 || $i == count($tests_to_show)) 1624 { 1625 echo PHP_EOL; 1626 } 1627 else 1628 { 1629 echo str_repeat(' ', $longest_test - strlen($test)); 1630 } 1631 } 1632 } 1633 } 1634 1635 // Disable this for now to cutdown on server resources and since not too useful 1636 // same info can be gathered from `phoronix-test-suite openbenchmarking-uploads` 1637 /* 1638 if(count($result_uploads = pts_openbenchmarking::result_uploads_from_this_ip()) > 0) 1639 { 1640 echo PHP_EOL . pts_client::cli_just_bold('Recent OpenBenchmarking.org Results From This IP:') . PHP_EOL; 1641 $t = array(); 1642 foreach($result_uploads as $id => $title) 1643 { 1644 $t[] = array(pts_client::cli_colored_text($id, 'gray', true), $title); 1645 1646 if(count($t) == 5) 1647 { 1648 break; 1649 } 1650 } 1651 echo pts_user_io::display_text_table($t, ' ') . PHP_EOL; 1652 } 1653 */ 1654 echo PHP_EOL; 1655 1656 $similar_tests = array(); 1657 if(!empty($passed_args)) 1658 { 1659 foreach(pts_arrays::to_array($passed_args) as $passed_arg) 1660 { 1661 $arg_soundex = soundex($passed_arg); 1662 $arg_save_identifier_like = pts_test_run_manager::clean_save_name($passed_arg); 1663 1664 foreach(pts_openbenchmarking::linked_repositories() as $repo) 1665 { 1666 $repo_index = pts_openbenchmarking::read_repository_index($repo); 1667 1668 foreach(array('tests', 'suites') as $type) 1669 { 1670 if(isset($repo_index[$type]) && is_array($repo_index[$type])) 1671 { 1672 foreach(array_keys($repo_index[$type]) as $identifier) 1673 { 1674 if(soundex($identifier) == $arg_soundex) 1675 { 1676 pts_arrays::unique_push($similar_tests, array($identifier, ' [' . ucwords(substr($type, 0, -1)) . ']')); 1677 } 1678 else if(isset($passed_arg[3]) && strpos($identifier, $passed_arg) !== false) 1679 { 1680 pts_arrays::unique_push($similar_tests, array($identifier, ' [' . ucwords(substr($type, 0, -1)) . ']')); 1681 } 1682 } 1683 } 1684 } 1685 } 1686 1687 foreach(pts_results::saved_test_results() as $result) 1688 { 1689 if(soundex($result) == $arg_soundex || (isset($passed_arg[3]) && strpos($identifier, $arg_save_identifier_like) !== false)) 1690 { 1691 pts_arrays::unique_push($similar_tests, array($result, ' [Test Result]')); 1692 } 1693 } 1694 1695 if(strpos($passed_arg, '-') !== false) 1696 { 1697 $possible_identifier = str_replace('-', '', $passed_arg); 1698 if(pts_test_profile::is_test_profile($possible_identifier)) 1699 { 1700 pts_arrays::unique_push($similar_tests, array($possible_identifier, ' [Test]')); 1701 } 1702 } 1703 if($passed_arg != ($possible_identifier = strtolower($passed_arg))) 1704 { 1705 if(pts_test_profile::is_test_profile($possible_identifier)) 1706 { 1707 pts_arrays::unique_push($similar_tests, array($possible_identifier, ' [Test]')); 1708 } 1709 } 1710 } 1711 } 1712 if(count($similar_tests) > 0) 1713 { 1714 echo pts_client::cli_just_bold('Possible Suggestions:') . PHP_EOL; 1715 //$similar_tests = array_unique($similar_tests); 1716 if(isset($similar_tests[12])) 1717 { 1718 // lots of tests... trim it down 1719 $similar_tests = array_rand($similar_tests, 12); 1720 } 1721 echo pts_user_io::display_text_table($similar_tests, '- ') . PHP_EOL . PHP_EOL; 1722 } 1723 1724 if($showed_recent_results == false) 1725 { 1726 echo 'See available tests to run by visiting OpenBenchmarking.org or running:' . PHP_EOL . PHP_EOL; 1727 echo ' phoronix-test-suite list-tests' . PHP_EOL . PHP_EOL; 1728 echo 'Tests can be installed by running:' . PHP_EOL . PHP_EOL; 1729 echo ' phoronix-test-suite install <test-name>' . PHP_EOL . PHP_EOL; 1730 } 1731 } 1732 public static function get_sent_command() 1733 { 1734 return self::$sent_command; 1735 } 1736 public static function handle_sent_command(&$sent_command, &$argv, &$argc) 1737 { 1738 self::$sent_command = $sent_command; 1739 if(is_file(PTS_PATH . 'pts-core/commands/' . $sent_command . '.php') == false) 1740 { 1741 $replaced = false; 1742 1743 if(pts_module::valid_run_command($sent_command)) 1744 { 1745 $replaced = true; 1746 } 1747 else if(isset($argv[1]) && strpos($argv[1], '.openbenchmarking') !== false && is_readable($argv[1])) 1748 { 1749 $sent_command = 'openbenchmarking_launcher'; 1750 $argv[2] = $argv[1]; 1751 $argc = 3; 1752 $replaced = true; 1753 } 1754 else 1755 { 1756 $aliases = pts_storage_object::read_from_file(PTS_CORE_STORAGE, 'command_alias_list'); 1757 if($aliases == null) 1758 { 1759 $aliases = pts_documentation::client_commands_aliases(); 1760 } 1761 1762 if(isset($aliases[$sent_command])) 1763 { 1764 $sent_command = $aliases[$sent_command]; 1765 $replaced = true; 1766 } 1767 } 1768 1769 if($replaced == false) 1770 { 1771 // Show help command, since there are no valid commands 1772 $sent_command = 'help'; 1773 } 1774 } 1775 else 1776 { 1777 $replaced = true; 1778 } 1779 1780 return $replaced; 1781 } 1782 public static function current_command() 1783 { 1784 return self::$current_command; 1785 } 1786 public static function terminal_width() 1787 { 1788 static $terminal_width = null; 1789 1790 // XXX As of PTS 8.6.1, no longer caching this as not sure it makes sense to do... So be more responsive about terminal resizing 1791 // Or at least cache on Windows where powershell calls can take longer 1792 if(!phodevi::is_windows() || $terminal_width == null) 1793 { 1794 $terminal_width = 80; 1795 1796 if(phodevi::is_windows()) 1797 { 1798 // Powershell defaults to 120 1799 $terminal_width = trim(shell_exec('powershell "(get-host).UI.RawUI.MaxWindowSize.width"')); 1800 } 1801 else if(pts_client::read_env('TERMINAL_WIDTH') != false && is_numeric(pts_client::read_env('TERMINAL_WIDTH')) >= 20) 1802 { 1803 $terminal_width = pts_client::read_env('TERMINAL_WIDTH'); 1804 } 1805 else if(pts_client::executable_in_path('stty')) 1806 { 1807 $terminal_width = explode(' ', trim(shell_exec('stty size 2>&1'))); 1808 1809 if(count($terminal_width) == 2 && is_numeric($terminal_width[1]) && $terminal_width[1] >= 40) 1810 { 1811 $terminal_width = $terminal_width[1]; 1812 } 1813 else 1814 { 1815 $terminal_width = 80; 1816 } 1817 } 1818 else if(pts_client::executable_in_path('tput')) 1819 { 1820 $terminal_width = trim(shell_exec('tput cols 2>&1')); 1821 1822 if(is_numeric($terminal_width) && $terminal_width > 1) 1823 { 1824 $terminal_width = $terminal_width; 1825 } 1826 else 1827 { 1828 $terminal_width = 80; 1829 } 1830 } 1831 } 1832 1833 return $terminal_width; 1834 } 1835 public static function terminal_height() 1836 { 1837 static $terminal_height = null; 1838 1839 if($terminal_height == null) 1840 { 1841 $terminal_height = 12; 1842 1843 if(phodevi::is_windows()) 1844 { 1845 $terminal_height = trim(shell_exec('powershell "(get-host).UI.RawUI.MaxWindowSize.height"')); 1846 } 1847 else if(pts_client::executable_in_path('stty')) 1848 { 1849 $th = explode(' ', trim(shell_exec('stty size 2>&1'))); 1850 1851 if(count($th) == 2 && is_numeric($th[0]) && $th[0] >= 1) 1852 { 1853 $terminal_height = $th[1]; 1854 } 1855 } 1856 else if(pts_client::executable_in_path('tput')) 1857 { 1858 $th = trim(shell_exec('tput lines 2>&1')); 1859 1860 if(is_numeric($th) && $th > 1) 1861 { 1862 $terminal_height = $th; 1863 } 1864 } 1865 } 1866 1867 return $terminal_height; 1868 } 1869 public static function is_process_running($process) 1870 { 1871 $running = null; 1872 if(phodevi::is_linux() && pts_client::executable_in_path('ps')) 1873 { 1874 // Checks if process is running on the system 1875 $ps = shell_exec('ps -C ' . strtolower($process) . ' 2>&1'); 1876 if(strpos($ps, 'unrecognized option') === false) 1877 { 1878 $running = trim(str_replace(array('PID', 'TTY', 'TIME', 'CMD'), '', $ps)); 1879 } 1880 } 1881 else if(phodevi::is_solaris()) 1882 { 1883 // Checks if process is running on the system 1884 $ps = shell_exec('ps -ef 2>&1'); 1885 $running = strpos($ps, ' ' . strtolower($process)) != false ? 'TRUE' : null; 1886 } 1887 else if(pts_client::executable_in_path('ps') != false) 1888 { 1889 // Checks if process is running on the system 1890 $ps = shell_exec('ps -ax 2>&1'); 1891 if(strpos($ps, 'unrecognized option') === false) 1892 { 1893 $running = strpos($ps, strtolower($process)) != false ? 'TRUE' : null; 1894 } 1895 } 1896 1897 return !empty($running); 1898 } 1899 public static function parse_value_string_double_identifier($value_string) 1900 { 1901 // i.e. with PRESET_OPTIONS='stream.run-type=Add' 1902 $values = array(); 1903 1904 foreach(explode(';', $value_string) as $preset) 1905 { 1906 if(count($preset = pts_strings::trim_explode('=', $preset)) == 2) 1907 { 1908 $dot = strrpos($preset[0], '.'); 1909 if($dot !== false && ($test = substr($preset[0], 0, $dot)) != null && ($option = substr($preset[0], ($dot + 1))) != null) 1910 { 1911 $values[$test][$option] = $preset[1]; 1912 } 1913 } 1914 } 1915 1916 return $values; 1917 } 1918 public static function create_temporary_file($file_extension = null) 1919 { 1920 $temp_file = tempnam(pts_client::temporary_directory(), 'PTS'); 1921 1922 if($file_extension) 1923 { 1924 $extended_file = pts_client::temporary_directory() . '/' . basename($temp_file) . $file_extension; 1925 1926 if(rename($temp_file, $extended_file)) 1927 { 1928 $temp_file = $extended_file; 1929 } 1930 } 1931 1932 return $temp_file; 1933 } 1934 public static function create_temporary_directory($prefix = null, $large_file_support = false) 1935 { 1936 $tmpdir = pts_client::temporary_directory(); 1937 if($large_file_support && disk_free_space(PTS_USER_PATH) > disk_free_space($tmpdir)) 1938 { 1939 $tmpdir = PTS_USER_PATH . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR; 1940 pts_file_io::mkdir($tmpdir); 1941 } 1942 1943 do 1944 { 1945 $randname = '/pts-' . $prefix . rand(0, 9999); 1946 } 1947 while(is_dir($tmpdir . $randname)); 1948 1949 mkdir($tmpdir . $randname); 1950 1951 return $tmpdir . $randname . '/'; 1952 } 1953 public static function temporary_directory() 1954 { 1955 return sys_get_temp_dir(); 1956 } 1957 public static function read_env($var) 1958 { 1959 return getenv($var); 1960 } 1961 public static function pts_set_environment_variable($name, $value) 1962 { 1963 // Sets an environmental variable 1964 return getenv($name) == false && putenv($name . '=' . $value); 1965 } 1966 public static function shell_exec($exec, $extra_vars = null) 1967 { 1968 // Same as shell_exec() but with the PTS env variables added in 1969 // Convert pts_client::environmental_variables() into shell export variable syntax 1970 1971 $var_string = ''; 1972 $extra_vars = ($extra_vars == null ? pts_client::environmental_variables() : array_merge(pts_client::environmental_variables(), $extra_vars)); 1973 1974 foreach(array_keys($extra_vars) as $key) 1975 { 1976 if(phodevi::is_windows()) 1977 { 1978 $v = str_replace('"', '', trim($extra_vars[$key])); 1979 if(substr($v, -1) == '\\') 1980 { echo $v; 1981 $v = substr($v, 0, -1); 1982 } 1983 $var_string .= 'setx ' . $key . ' "' . $v . '";'; 1984 } 1985 else 1986 { 1987 $var_string .= 'export ' . $key . '="' . str_replace(' ', '\ ', trim($extra_vars[$key])) . '";'; 1988 } 1989 } 1990 $var_string .= ' '; 1991 1992 return shell_exec($var_string . $exec); 1993 } 1994 public static function get_path() 1995 { 1996 $path = pts_client::read_env('PATH'); 1997 if(empty($path) || $path == ':') 1998 { 1999 if(phodevi::is_windows()) 2000 { 2001 $path = 'C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Users\\' . getenv('USERNAME') . '\AppData\Local\Microsoft\WindowsApps;'; 2002 } 2003 else 2004 { 2005 $path = '/bin:/usr/sbin:/usr/bin:/usr/local/bin:/usr/pkg/bin:/usr/games'; 2006 } 2007 } 2008 if(phodevi::is_windows()) 2009 { 2010 $possible_paths_to_add = array('C:\Users\\' . getenv('USERNAME') . '\AppData\Local\Programs\Python\Python36-32', 2011 'C:\Users\\' . getenv('USERNAME') . '\AppData\Local\Programs\Python\Python37', 2012 'C:\Users\\' . getenv('USERNAME') . '\AppData\Local\Programs\Python\Python38', 2013 'C:\Users\\' . getenv('USERNAME') . '\AppData\Local\Programs\Python\Python39', 2014 'C:\Python27', 2015 'C:\Go\bin', 2016 'C:\Strawberry\perl\bin', 2017 pts_file_io::glob('C:\*\NVIDIA*\NVSMI'), // NVIDIA SMI 2018 pts_file_io::glob('C:\*\R\R-*\bin'), 2019 pts_file_io::glob('C:\*\Java\jdk-*\bin'), pts_file_io::glob('C:\*\ojdkbuild\java-*\bin'), pts_file_io::glob('C:\*\Java\jre-*\bin'), 2020 'C:\cygwin64\bin', 2021 pts_file_io::glob('C:\Program*\LLVM\bin'), 2022 pts_file_io::glob('C:\Program*\CMake\bin'), 2023 pts_file_io::glob('C:\Program*\WinRAR') 2024 ); 2025 foreach($possible_paths_to_add as $path_check) 2026 { 2027 if(is_array($path_check)) 2028 { 2029 // if it's an array it came from glob so no need to re-check if is_dir() 2030 foreach($path_check as $sub_check) 2031 { 2032 if(strpos($path, $sub_check) == false) 2033 { 2034 $path .= ';' . $sub_check; 2035 } 2036 } 2037 } 2038 else if(is_dir($path_check) && strpos($path, $path_check) == false) 2039 { 2040 $path .= ';' . $path_check; 2041 } 2042 } 2043 } 2044 else 2045 { 2046 // Fedora OpenMPI path not often in PATH by default 2047 $ds = array('/usr/lib64/openmpi/bin', 2048 '/usr/local/mpi/openmpi/bin' 2049 ); 2050 foreach($ds as $d) 2051 { 2052 if(is_dir($d) && strpos($path, $d) === false) 2053 { 2054 $path .= ':' . $d; 2055 } 2056 } 2057 } 2058 2059 return $path; 2060 } 2061 public static function get_path_separator() 2062 { 2063 return phodevi::is_windows() ? ';' : ':'; 2064 } 2065 public static function executable_in_path($executable, $ignore_paths_with = false) 2066 { 2067 static $cache = null; 2068 2069 if(!isset($cache[$executable]) || empty($cache[$executable]) || $ignore_paths_with) 2070 { 2071 $path = pts_client::get_path(); 2072 $paths = pts_strings::trim_explode(pts_client::get_path_separator(), $path); 2073 $executable_path = false; 2074 2075 foreach($paths as $path) 2076 { 2077 $path = pts_strings::add_trailing_slash($path); 2078 2079 if(is_executable($path . $executable)) 2080 { 2081 if($ignore_paths_with && stripos($path, $ignore_paths_with) !== false) 2082 { 2083 continue; 2084 } 2085 2086 $executable_path = $path . $executable; 2087 break; 2088 } 2089 } 2090 2091 if($ignore_paths_with) 2092 { 2093 // Don't cache calls using the $ignore_paths_with parameter 2094 return $executable_path; 2095 } 2096 2097 $cache[$executable] = $executable_path; 2098 } 2099 if($cache[$executable] == false && phodevi::is_windows() && substr($executable, -4) != '.exe') 2100 { 2101 // See if there is .exe match most likely, e.g. Java, Python, etc. 2102 $cache[$executable] = pts_client::executable_in_path($executable . '.exe'); 2103 } 2104 2105 return $cache[$executable]; 2106 } 2107 public static function test_for_result_viewer_connection($port) 2108 { 2109 if(is_numeric($port)) 2110 { 2111 $dynamic_urls_to_try = array(); 2112 2113 // Due to the way PHP web server is handled depending upon if remote allowed, 2114 // both URLs need to be tried 2115 $dynamic_urls_to_try[] = 'http://localhost:' . $port; 2116 $dynamic_urls_to_try[] = 'http://127.0.0.1:' . $port; 2117 2118 foreach($dynamic_urls_to_try as $base_url) 2119 { 2120 if(pts_network::http_get_contents($base_url . '/index.php?PTS', false, false, false, false, 3) == 'PTS') 2121 { 2122 return $base_url; 2123 } 2124 } 2125 } 2126 2127 return false; 2128 } 2129 public static function display_result_view($result_file, $auto_open = false, $prompt_text = null) 2130 { 2131 if(defined('PHOROMATIC_PROCESS')) 2132 { 2133 return false; 2134 } 2135 2136 if(!is_object($result_file)) 2137 { 2138 $result_file = new pts_result_file($result_file); 2139 } 2140 2141 if(!phodevi::is_display_server_active()) 2142 { 2143 $prompt_text = !empty($prompt_text) ? $prompt_text : 'Do you want to view the test results?'; 2144 $txt_results = $auto_open || pts_user_io::prompt_bool_input($prompt_text, true); 2145 if($txt_results) 2146 { 2147 echo pts_result_file_output::result_file_to_text($result_file, pts_client::terminal_width()); 2148 } 2149 } 2150 else 2151 { 2152 if(TIME_PTS_LAUNCHED > (time() - 6)) 2153 { 2154 // Avoid a race condition on start-up if the dynamic result viewer PHP server isn't yet active 2155 // and running a command like 'refresh-graphs' where you may be viewing a result right away 2156 sleep(5); 2157 } 2158 2159 $ports_to_try = array(); 2160 if(pts_client::$web_result_viewer_active) 2161 { 2162 $ports_to_try[] = pts_client::$web_result_viewer_active; 2163 } 2164 if(($restored_port = pts_storage_object::read_from_file(PTS_CORE_STORAGE, 'last_web_result_viewer_active_port')) != false && is_numeric($restored_port) && $restored_port > 20) 2165 { 2166 $ports_to_try[] = $restored_port; 2167 } 2168 2169 foreach($ports_to_try as $try_port) 2170 { 2171 if(($base_url = pts_client::test_for_result_viewer_connection($try_port))) 2172 { 2173 pts_client::$has_used_modern_result_viewer = true; 2174 pts_client::$last_result_view_url = $base_url . '/result/' . $result_file->get_identifier(); 2175 $length_browser_open = pts_client::display_web_page(pts_client::$last_result_view_url, $prompt_text, true, $auto_open); 2176 return true; 2177 } 2178 } 2179 2180 // Failed to start/find the dynamic result viewer... 2181 trigger_error('Dynamic result viewer not running or inaccessible', E_USER_WARNING); 2182 $prompt_text = !empty($prompt_text) ? $prompt_text : 'Do you want to view the text-based test results?'; 2183 $txt_results = $auto_open || pts_user_io::prompt_bool_input($prompt_text, true); 2184 if($txt_results) 2185 { 2186 echo pts_result_file_output::result_file_to_text($result_file, pts_client::terminal_width()); 2187 } 2188 } 2189 } 2190 public static function display_web_page($URL, $alt_text = null, $default_open = true, $auto_open = false) 2191 { 2192 if(!phodevi::is_display_server_active() || defined('PHOROMATIC_PROCESS')) 2193 { 2194 return -1; 2195 } 2196 2197 if($auto_open == false) 2198 { 2199 $view_results = pts_user_io::prompt_bool_input(($alt_text == null ? 'Do you want to view the results in your web browser' : $alt_text), $default_open); 2200 } 2201 else 2202 { 2203 $view_results = true; 2204 } 2205 2206 if($view_results) 2207 { 2208 static $browser = null; 2209 2210 if($browser == null) 2211 { 2212 $config_browser = pts_config::read_user_config('PhoronixTestSuite/Options/General/DefaultBrowser', null); 2213 2214 if($config_browser != null && (is_executable($config_browser) || ($config_browser = pts_client::executable_in_path($config_browser)))) 2215 { 2216 $browser = $config_browser; 2217 } 2218 else if(phodevi::is_windows()) 2219 { 2220 $windows_browsers = array( 2221 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe', 2222 'C:\Program Files (x86)\Mozilla Firefox\firefox.exe', 2223 //'C:\Program Files\internet explorer\iexplore.exe' 2224 ); 2225 2226 foreach($windows_browsers as $browser_test) 2227 { 2228 if(is_executable($browser_test)) 2229 { 2230 $browser = $browser_test; 2231 break; 2232 } 2233 } 2234 2235 if(!empty($browser)) 2236 { 2237 $browser = escapeshellarg($browser); 2238 } 2239 2240 if(substr($URL, 0, 1) == '\\') 2241 { 2242 $URL = 'file:///C:' . str_replace('/', '\\', $URL); 2243 } 2244 else if(substr($URL, 0, 2) == 'C:') 2245 { 2246 $URL = 'file:///' . str_replace('//', '/', str_replace('\\', '/', $URL)); 2247 } 2248 2249 2250 pts_client::$last_browser_launch_time = time(); 2251 $launch_time = microtime(true); 2252 if(empty($browser)) 2253 { 2254 // should allow the browser to be opened in Edge 2255 shell_exec('start ' . $URL . ''); 2256 } 2257 else 2258 { 2259 shell_exec($browser . ' "' . $URL . '"'); 2260 } 2261 pts_client::$last_browser_duration = microtime(true) - $launch_time; 2262 return -1; 2263 } 2264 else 2265 { 2266 $possible_browsers = array('x-www-browser', 'google-chrome', 'chromium', 'firefox', 'mozilla', 'iceweasel', 'konqueror', 'epiphany', 'midori', 'epiphany-browser', 'epiphany', 'falkon', 'qupzilla', 'open', 'xdg-open'); 2267 2268 // First try to see if a browser is already running and use that 2269 foreach($possible_browsers as &$b) 2270 { 2271 if(pts_client::is_process_running($b) && ($b = pts_client::executable_in_path($b))) 2272 { 2273 $browser = $b; 2274 break; 2275 } 2276 } 2277 2278 // Otherwise just find any browser available in PATH 2279 if($browser == null) 2280 { 2281 foreach($possible_browsers as &$b) 2282 { 2283 if(($b = pts_client::executable_in_path($b))) 2284 { 2285 $browser = $b; 2286 break; 2287 } 2288 } 2289 } 2290 } 2291 } 2292 2293 if($browser != null) 2294 { 2295 $launch_time = microtime(true); 2296 pts_client::$last_browser_launch_time = time(); 2297 shell_exec($browser . ' "' . $URL . '" 2> /dev/null &'); 2298 2299 // return how long the browser was opened, useful for trying to see if launched to an existing open browser or process stayed while viewing results 2300 pts_client::$last_browser_duration = microtime(true) - $launch_time; 2301 return pts_client::$last_browser_duration; 2302 } 2303 else 2304 { 2305 echo PHP_EOL . 'No Web Browser Found.' . PHP_EOL; 2306 } 2307 } 2308 2309 return -1; 2310 } 2311 public static function timed_function($function, $function_parameters, $time, $continue_while_true_function = false, $continue_while_true_function_parameters = null) 2312 { 2313 if(($time < 0.5 && $time != -1) || $time > 300) 2314 { 2315 return; 2316 } 2317 if($continue_while_true_function && $continue_while_true_function_parameters === null) 2318 { 2319 $continue_while_true_function_parameters = array(); 2320 } 2321 2322 if(function_exists('pcntl_fork') && function_exists('posix_setsid')) 2323 { 2324 $current_pid = function_exists('posix_getpid') ? posix_getpid() : -1; 2325 $pid = pcntl_fork(); 2326 2327 if($pid == -1) 2328 { 2329 trigger_error('Could not fork ' . $function . '.', E_USER_ERROR); 2330 } 2331 else if($pid) 2332 { 2333 self::$forked_pids[] = $pid; 2334 } 2335 else 2336 { 2337 posix_setsid(); 2338 $loop_continue = true; 2339 while($loop_continue && is_file(PTS_USER_LOCK) && ($continue_while_true_function === true || ($loop_continue = call_user_func_array($continue_while_true_function, $continue_while_true_function_parameters)))) 2340 { 2341 call_user_func_array($function, $function_parameters); 2342 2343 if($time > 0) 2344 { 2345 sleep($time); 2346 } 2347 else if($time == -1) 2348 { 2349 $loop_continue = false; 2350 } 2351 if($current_pid != -1 && !is_dir('/proc/' . $current_pid)) 2352 { 2353 exit; 2354 } 2355 clearstatcache(); 2356 } 2357 if(function_exists('posix_kill')) 2358 { 2359 posix_kill(posix_getpid(), SIGINT); 2360 } 2361 exit(0); 2362 } 2363 } 2364 else 2365 { 2366 if(is_array($function)) 2367 { 2368 $function = implode(':', $function); 2369 } 2370 2371 trigger_error('php-pcntl and php-posix must be installed for calling ' . $function . '.', E_USER_ERROR); 2372 } 2373 } 2374 public static function fork($fork_function, $fork_function_parameters = null) 2375 { 2376 if(!is_array($fork_function_parameters)) 2377 { 2378 $fork_function_parameters = array($fork_function_parameters); 2379 } 2380 2381 if(function_exists('pcntl_fork')) 2382 { 2383 $current_pid = function_exists('posix_getpid') ? posix_getpid() : -1; 2384 $pid = pcntl_fork(); 2385 2386 if($pid == -1) 2387 { 2388 trigger_error('Could not fork ' . $fork_function . '.', E_USER_ERROR); 2389 } 2390 else if($pid) 2391 { 2392 // PARENT 2393 self::$forked_pids[] = $pid; 2394 return true; 2395 } 2396 else 2397 { 2398 // CHILD 2399 // posix_setsid(); 2400 call_user_func_array($fork_function, $fork_function_parameters); 2401 if(function_exists('posix_kill')) 2402 { 2403 posix_kill(posix_getpid(), SIGINT); 2404 } 2405 exit(0); 2406 } 2407 } 2408 else 2409 { 2410 // No PCNTL Support 2411 call_user_func_array($fork_function, $fork_function_parameters); 2412 } 2413 2414 return false; 2415 } 2416 public static function code_error_handler($error_code, $error_string, $error_file, $error_line) 2417 { 2418 /*if(!(error_reporting() & $error_code)) 2419 { 2420 return; 2421 }*/ 2422 2423 switch($error_code) 2424 { 2425 case E_USER_ERROR: 2426 $error_type = 'PROBLEM'; 2427 if(pts_client::is_debug_mode() == false) 2428 { 2429 $error_file = null; 2430 $error_line = 0; 2431 } 2432 break; 2433 case E_USER_NOTICE: 2434 if(pts_client::is_debug_mode() == false) 2435 { 2436 return; 2437 } 2438 $error_type = 'NOTICE'; 2439 break; 2440 case E_USER_WARNING: 2441 $error_type = 'NOTICE'; // Yes, report warnings as a notice 2442 if(pts_client::is_debug_mode() == false) 2443 { 2444 $error_file = null; 2445 $error_line = 0; 2446 } 2447 break; 2448 case E_ERROR: 2449 case E_PARSE: 2450 $error_type = 'ERROR'; 2451 break; 2452 case E_WARNING: 2453 case E_NOTICE: 2454 $error_type = 'NOTICE'; 2455 if(($s = strpos($error_string, 'Undefined ')) !== false && ($x = strpos($error_string, ': ', $s)) !== false) 2456 { 2457 $error_string = 'Undefined: ' . substr($error_string, ($x + 2)); 2458 } 2459 else if(strpos($error_string, 'Unable to find the socket transport') !== false || strpos($error_string, 'SSL: Connection reset') !== false) 2460 { 2461 $error_string = 'PHP OpenSSL support is needed to handle HTTPS downloads.'; 2462 $error_file = null; 2463 $error_line = null; 2464 } 2465 else 2466 { 2467 $ignore_errors = array( 2468 'Name or service not known', 2469 'HTTP request failed', 2470 'fopen', 2471 'fsockopen', 2472 'file_get_contents', 2473 'failed to connect', 2474 'unable to connect', 2475 'directory not empty', 2476 '_lock', // likely multi-process issue, etc for unlinking lock 2477 ); 2478 2479 foreach($ignore_errors as $error_check) 2480 { 2481 if(stripos($error_string, $error_check) !== false) 2482 { 2483 return; 2484 } 2485 } 2486 } 2487 break; 2488 default: 2489 $error_type = $error_code; 2490 break; 2491 } 2492 2493 if(pts_client::$pts_logger != false) 2494 { 2495 pts_client::$pts_logger->report_error($error_type, $error_string, $error_file, $error_line); 2496 } 2497 2498 if(pts_client::$display != false) 2499 { 2500 pts_client::$display->triggered_system_error($error_type, $error_string, $error_file, $error_line); 2501 } 2502 else 2503 { 2504 echo PHP_EOL . $error_string; 2505 2506 if($error_file != null && $error_line != null) 2507 { 2508 echo ' in ' . $error_file . ':' . $error_line; 2509 } 2510 2511 echo PHP_EOL; 2512 } 2513 2514 if($error_type == 'ERROR') 2515 { 2516 exit(1); 2517 } 2518 } 2519 public static function set_debug_mode($dmode) 2520 { 2521 self::$debug_mode = ($dmode == true); 2522 } 2523 public static function is_debug_mode() 2524 { 2525 // debug mode for tests 2526 return self::$debug_mode == true; 2527 } 2528 public static function update_download_speed_average($download_size, $elapsed_time) 2529 { 2530 if(self::$download_speed_average_count == -1) 2531 { 2532 self::load_download_speed_averages(); 2533 } 2534 2535 $download_speed = floor($download_size / $elapsed_time); // bytes per second 2536 2537 if(self::$download_speed_average_count > 0 && self::$download_speed_average_speed > 0) 2538 { 2539 // bytes per second 2540 self::$download_speed_average_speed = floor(((self::$download_speed_average_speed * self::$download_speed_average_count) + $download_speed) / (self::$download_speed_average_count + 1)); 2541 self::$download_speed_average_count++; 2542 } 2543 else 2544 { 2545 self::$download_speed_average_speed = $download_speed; 2546 self::$download_speed_average_count = 1; 2547 } 2548 } 2549 public static function get_average_download_speed() 2550 { 2551 if(self::$download_speed_average_count == -1) 2552 { 2553 self::load_download_speed_averages(); 2554 } 2555 2556 return self::$download_speed_average_speed; 2557 } 2558 private static function load_download_speed_averages() 2559 { 2560 self::$download_speed_average_count = pts_storage_object::read_from_file(PTS_CORE_STORAGE, 'download_average_count'); 2561 self::$download_speed_average_speed = pts_storage_object::read_from_file(PTS_CORE_STORAGE, 'download_average_speed'); 2562 } 2563 public static function save_download_speed_averages() 2564 { 2565 pts_storage_object::set_in_file(PTS_CORE_STORAGE, 'download_average_count', self::$download_speed_average_count); 2566 pts_storage_object::set_in_file(PTS_CORE_STORAGE, 'download_average_speed', self::$download_speed_average_speed); 2567 } 2568} 2569 2570// Some extra magic 2571set_error_handler(array('pts_client', 'code_error_handler')); 2572 2573if(PTS_IS_CLIENT && (PTS_IS_DEV_BUILD || pts_client::is_debug_mode())) 2574{ 2575 // Enable more verbose error reporting only when PTS is in development with milestone (alpha/beta) releases but no release candidate (r) or gold versions 2576 error_reporting(E_ALL | E_NOTICE | E_STRICT); 2577} 2578 2579?> 2580