1<?php 2 3/* 4 Phoronix Test Suite 5 URLs: http://www.phoronix.com, http://www.phoronix-test-suite.com/ 6 Copyright (C) 2015 - 2020, Phoronix Media 7 Copyright (C) 2015 - 2020, 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 linux_perf extends pts_module_interface 24{ 25 const module_name = 'Linux Perf Framework Reporter'; 26 const module_version = '1.1.0'; 27 const module_description = 'Setting LINUX_PERF=1 will auto-load and enable this Phoronix Test Suite module. The module also depends upon running a modern Linux kernel (supporting perf) and that the perf binary is available via standard system paths.'; 28 const module_author = 'Michael Larabel'; 29 30 private static $result_identifier; 31 private static $successful_test_run; 32 private static $std_output; 33 private static $tmp_file; 34 35 public static function module_environmental_variables() 36 { 37 return array('LINUX_PERF'); 38 } 39 public static function module_info() 40 { 41 return null; 42 } 43 public static function __run_manager_setup(&$test_run_manager) 44 { 45 // Verify LINUX_PERF is set, `perf` can be found, and is Linux 46 if(getenv('LINUX_PERF') == 0 || !pts_client::executable_in_path('perf') || !phodevi::is_linux()) 47 { 48 return pts_module::MODULE_UNLOAD; // This module doesn't have anything else to do 49 } 50 echo PHP_EOL . 'Linux PERF Monitoring Enabled.' . PHP_EOL . PHP_EOL; 51 52 // This module won't be too useful if you're not saving the results to see the graphs 53 $test_run_manager->force_results_save(); 54 } 55 public static function __pre_run_process(&$test_run_manager) 56 { 57 // Copy the current result identifier 58 self::$result_identifier = $test_run_manager->get_results_identifier(); 59 } 60 public static function __pre_test_run(&$test_run_request) 61 { 62 // Set the perf command to pass in front of all tests to run 63 self::$tmp_file = tempnam(sys_get_temp_dir(), 'perf'); 64 // -d or below is more exhaustive list 65 $test_run_request->exec_binary_prepend = 'perf stat -e branches,branch-misses,cache-misses,cache-references,cycles,instructions,cs,cpu-clock,page-faults,duration_time,task-clock,L1-dcache-load-misses,L1-dcache-loads,L1-dcache-prefetches,L1-icache-load-misses,context-switches,cpu-migrations,branch-loads,branch-load-misses,dTLB-loads,dTLB-load-misses,iTLB-load-misses,iTLB-loads -o ' . self::$tmp_file . ' '; 66 } 67 public static function __post_test_run_success($test_run_request) 68 { 69 // Base the new result object/graph off of what just ran 70 self::$successful_test_run = clone $test_run_request; 71 72 // For now the current implementation is just copying the perf output for the last test run, but rather easily could be adapted to take average of all test runs, etc 73 //self::$std_output = $test_run_request->test_result_standard_output; 74 self::$std_output = file_get_contents(self::$tmp_file); 75 pts_file_io::unlink(self::$tmp_file); 76 } 77 public static function __post_test_run_process(&$result_file) 78 { 79 if(self::$successful_test_run && !empty(self::$std_output)) 80 { 81 if(($x = strpos(self::$std_output, 'Performance counter stats for')) === 0) 82 { 83 // No output found 84 return; 85 } 86 self::$std_output = substr(self::$std_output, $x); 87 88 // Items to find and report from the perf output 89 $perf_stats = array( 90 'page-faults' => array('Page Faults', 'Faults', 'LIB'), 91 'context-switches' => array('Context Switches', 'Context Switches', 'LIB'), 92 'cpu-migrations' => array('CPU Migrations', 'CPU Migrations', 'LIB'), 93 'branches' => array('Branches', 'Branches', ''), 94 'branch-misses' => array('Branch Misses', 'Branch Misses', 'LIB'), 95 'seconds user' => array('User Time', 'Seconds', 'LIB'), 96 'seconds sys' => array('Kernel/System Time', 'Seconds', 'LIB'), 97 'stalled-cycles-frontend' => array('Stalled Cycles Front-End', 'Cycles Idle', 'LIB'), 98 'stalled-cycles-backend' => array('Stalled Cycles Back-End', 'Cycles Idle', 'LIB'), 99 'L1-dcache-loads' => array('L1d Loads', 'L1d Cache Loads', ''), 100 'L1-icache-loads' => array('L1i Loads', 'L1i Cache Loads', ''), 101 'L1-dcache-load-misses' => array('L1d Load Misses', 'L1 Data Cache Load Misses', 'LIB'), 102 'L1-icache-load-misses' => array('L1i Load Misses', 'L1 Instruction Cache Load Misses', 'LIB'), 103 'cache-misses' => array('Cache Misses', 'Cache Misses', 'LIB'), 104 'branch-load-misses' => array('Branch Load Misses', 'Branch Load Misses', 'LIB'), 105 'dTLB-load-misses' => array('dTLB Load Misses', 'dTLB Load Misses', 'LIB'), 106 'ex_ret_mmx_fp_instr.sse_instr' => array('SSE Instructions', 'SSE Instructions', ''), 107 'fp_ret_sse_avx_ops.all' => array('SSE+AVX Instructions', 'AVX Instructions', ''), 108 'instructions' => array('Instructions', 'Instructions', 'LIB'), 109 ); 110 111 foreach($perf_stats as $string_to_match => $data) 112 { 113 list($pretty_string, $units, $hib_or_lib) = $data; 114 if(($x = strpos(self::$std_output, $string_to_match)) !== false) 115 { 116 $sout = substr(self::$std_output, 0, $x); 117 $sout = str_replace(',', '', trim(substr($sout, (strrpos($sout, PHP_EOL) + 1)))); 118 119 if(is_numeric($sout) && $sout > 0) 120 { 121 // Assemble the new result object for the matching perf item 122 $original_parent_hash = self::$successful_test_run->get_comparison_hash(true, false); 123 $test_result = clone self::$successful_test_run; 124 $test_result->test_profile->set_identifier(null); 125 $test_result->set_parent_hash($original_parent_hash); 126 127 // Description to show on graph 128 $test_result->set_used_arguments_description($pretty_string . ' (' . $test_result->get_arguments_description() . ')'); 129 130 // Make a unique string for XML result matching 131 $test_result->set_used_arguments('perf ' . $string_to_match . ' ' . $test_result->get_arguments()); 132 $test_result->test_profile->set_result_scale($units); 133 $test_result->test_profile->set_result_proportion($hib_or_lib); 134 $test_result->test_result_buffer = new pts_test_result_buffer(); 135 $test_result->test_result_buffer->add_test_result(self::$result_identifier, $sout); 136 $test_result->set_parent_hash(self::$successful_test_run->get_comparison_hash(true, false)); 137 $result_file->add_result($test_result); 138 } 139 } 140 } 141 } 142 143 // Reset to be safe 144 self::$successful_test_run = null; 145 self::$std_output = null; 146 } 147} 148?> 149