def find_gcc_compiler(): best = None for program in GCC_BINARIES: try: # currently the arm version of gcc from homebrew doesn't come with libasan and libubsan in which case we don't want to choose gcc if platform.system() == 'Darwin' and platform.machine() == 'arm64': args = [ program, '-xc++', '-fsanitize=address', '-fsanitize=undefined', '-', '-o', '/dev/null' ] log_command(args, 2) assert (subprocess.run(args, timeout=10, input="int main() { } ", text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).returncode == 0) args = [program, '-dM', '-fopenmp', '-E', '-'] log_command(args, 2) output = subprocess.run(args, input='', timeout=10, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8', errors='utf-8') if output.returncode == 0: lines = output.stdout.split('\n') matches = [ re.match('^#define (\\w+) (.*)$', line) for line in lines ] macros = { match.group(1): match.group(2) for match in matches if match is not None } if '__clang__' not in macros and '_OPENMP' in macros: # We don't want to choose clang in any case try: version = (int(macros['__GNUC__']), int(macros['__GNUC_MINOR__']), int(macros['__GNUC_PATCHLEVEL__'])) if version[0] >= MIN_GCC and (best is None or best[1] < version): best = (program, version) except TypeError: pass except FileNotFoundError: pass except AssertionError: pass if best is not None: return GccCompiler(best[0])
def run(self, config, args: List[str], timeout: float) -> AsanRunnerOutput: env = os.environ.copy() env.update(self.env) if 'ASAN_OPTIONS' in env: env['ASAN_OPTIONS'] += ':log_path=/tmp/asan.log' else: env['ASAN_OPTIONS'] = 'log_path=/tmp/asan.log' ppc_output_read, ppc_output_write = os.pipe() env['PPC_OUTPUT'] = str(ppc_output_write) log_command(args) process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL, env=env, encoding='utf-8', errors='utf-8', pass_fds=(ppc_output_write, )) os.close(ppc_output_write) try: stdout, stderr = process.communicate(None, timeout=timeout) run_successful = process.returncode == 0 timed_out = False except subprocess.TimeoutExpired: timed_out = True run_successful = False try: # Ask nicely to terminate before killing process.terminate() stdout, stderr = process.communicate(None, timeout=1) except subprocess.TimeoutExpired: process.kill() stdout, stderr = process.communicate(None) # Read Asan output if os.path.exists(f"/tmp/asan.log.{process.pid}"): with open(f"/tmp/asan.log.{process.pid}") as f: asanoutput = f.read() # Try to delete the output file os.remove(f"/tmp/asan.log.{process.pid}") else: asanoutput = None if run_successful: output = os.fdopen(ppc_output_read, 'r').read() results = config.parse_output(output) else: os.close(ppc_output_read) results = [] return AsanRunnerOutput(run_successful, timed_out, stdout, stderr, timeout, asanoutput, *results)
def is_valid(self) -> bool: try: args = [self.program, '-dM', '-fopenmp', '-E', '-'] log_command(args, 2) return subprocess.run(args, input='', timeout=10, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8', errors='utf-8').returncode == 0 except AssertionError: return False except subprocess.TimeoutExpired: sys.exit( 'Testing compiler took too much time, killed the process.')
def find_nvcc_compiler(): best = None for program in NVCC_BINARIES: try: log_command([program], 2) output = subprocess.run([program], input='', timeout=10, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8', errors='utf-8') if output.returncode == 1: best = (program, 0) except FileNotFoundError: pass if best is not None: return NvccCompiler(best[0])
def run(self, config, args: List[str], timeout: Optional[float]) -> RunnerOutput: env = os.environ.copy() ppc_output_read, ppc_output_write = os.pipe() env['PPC_OUTPUT'] = str(ppc_output_write) env['PPC_PERF'] = 'default' log_command(args) process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL, env=env, encoding='utf-8', errors='utf-8', pass_fds=(ppc_output_write, )) os.close(ppc_output_write) try: stdout, stderr = process.communicate(None, timeout=timeout) run_successful = process.returncode == 0 timed_out = False except subprocess.TimeoutExpired: run_successful = False timed_out = True try: # Ask nicely to terminate before killing process.terminate() stdout, stderr = process.communicate(None, timeout=1) except subprocess.TimeoutExpired: process.kill() stdout, stderr = process.communicate(None) if run_successful: output = os.fdopen(ppc_output_read, 'r').read() results = config.parse_output(output) else: os.close(ppc_output_read) results = [] return RunnerOutput(run_successful, timed_out, stdout, stderr, timeout, *results)
def __get_version(program: str): try: args = [program, '-dM', '-fopenmp', '-E', '-'] if platform.system() == 'Darwin': args = [ program, '-dM', '-Xpreprocessor', '-fopenmp', '-E', '-' ] log_command(args, 2) output = subprocess.run(args, input='', timeout=10, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8', errors='utf-8') if output.returncode == 0: lines = output.stdout.split('\n') matches = [ re.match('^#define (\\w+) (.*)$', line) for line in lines ] macros = { match.group(1): match.group(2) for match in matches if match is not None } try: apple = True if 'Apple' in macros['__VERSION__'] else False except: apple = None if '__clang__' in macros and '_OPENMP' in macros: try: return [(int(macros['__clang_major__']), int(macros['__clang_minor__']), int(macros['__clang_patchlevel__'])), apple] except TypeError: pass except FileNotFoundError: pass return [None, None]
def compile(self, out_file: str = 'a.out') -> CompilerOutput: try: args = [self.program ] + self.common_flags + self.flags + self.sources + [ '-o', out_file ] log_command(args) result = subprocess.run(args, timeout=10, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8', errors='utf-8') except subprocess.TimeoutExpired: return CompilerOutput( '', 'Compilation process took too much time, killed the process', -1) return CompilerOutput(result.stdout[:MAX_COMPILER_OUTPUT], result.stderr[:MAX_COMPILER_OUTPUT], result.returncode)
def is_valid(self) -> bool: try: args = [self.program, '-dM', '-fopenmp', '-E', '-'] if isinstance(self, ClangCompiler) and platform.system() == 'Darwin': args = [ self.program, '-dM', '-Xpreprocessor', '-fopenmp', '-E', '-' ] log_command(args) return subprocess.run(args, input='', timeout=10, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8', errors='utf-8').returncode == 0 except AssertionError: return False except subprocess.TimeoutExpired: sys.exit( 'Testing compiler took too much time, killed the process.')
def run(self, config, args: List[str], timeout: float) -> MemcheckRunnerOutput: env = os.environ.copy() env.update(self.env) ppc_output_read, ppc_output_write = os.pipe() env['PPC_OUTPUT'] = str(ppc_output_write) # Run with memcheck memcheck_output_file = tempfile.NamedTemporaryFile('r') args = [ 'cuda-memcheck', '--tool', self.tool, '--log-file', memcheck_output_file.name, '--error-exitcode', '1', '--prefix', ' ', '--print-limit', '1000', *(['--leak-check', 'full'] if self.tool == 'memcheck' else []), '--', ] + args log_command(args) process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL, env=env, encoding='utf-8', errors='utf-8', pass_fds=(ppc_output_write, )) os.close(ppc_output_write) try: stdout, stderr = process.communicate(None, timeout=timeout) run_successful = process.returncode == 0 timed_out = False except subprocess.TimeoutExpired: timed_out = True run_successful = False try: # Ask nicely to terminate before killing process.terminate() stdout, stderr = process.communicate(None, timeout=1) except subprocess.TimeoutExpired: process.kill() stdout, stderr = process.communicate(None) # Read memcheck output memcheckoutput = memcheck_output_file.read() no_outputs = [ ' CUDA-MEMCHECK\n LEAK SUMMARY: 0 bytes leaked in 0 allocations\n ERROR SUMMARY: 0 errors\n', ' CUDA-MEMCHECK\n RACECHECK SUMMARY: 0 hazards displayed (0 errors, 0 warnings) \n', ' CUDA-MEMCHECK\n ERROR SUMMARY: 0 errors\n', ] if memcheckoutput in no_outputs: memcheckoutput = None if run_successful: output = os.fdopen(ppc_output_read, 'r').read() results = config.parse_output(output) else: os.close(ppc_output_read) results = [] return MemcheckRunnerOutput(run_successful, timed_out, stdout, stderr, timeout, memcheckoutput, *results)
def run(self, config, args: List[str], timeout: Optional[float]) -> RunnerOutput: env = os.environ.copy() ppc_output_read, ppc_output_write = os.pipe() env['PPC_OUTPUT'] = str(ppc_output_write) env['PPC_PERF'] = 'default' # Run with nvprof nvprof_output_file = tempfile.NamedTemporaryFile('r') args = [ 'nvprof', '--csv', '--log-file', nvprof_output_file.name, '--normalized-time-unit', 's', '--print-gpu-trace', '--', ] + args log_command(args) process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL, env=env, encoding='utf-8', errors='utf-8', pass_fds=(ppc_output_write, )) os.close(ppc_output_write) try: stdout, stderr = process.communicate(None, timeout=timeout) run_successful = process.returncode == 0 timed_out = False except subprocess.TimeoutExpired: run_successful = False timed_out = True try: # Ask nicely to terminate before killing process.terminate() stdout, stderr = process.communicate(None, timeout=1) except subprocess.TimeoutExpired: process.kill() stdout, stderr = process.communicate(None) # Read nvprof output def get_scale(unit: str, base=1000) -> float: if unit == 'm': return base**-1 elif unit == 'K': return base**1 elif unit == 'M': return base**2 elif unit == 'G': return base**3 elif unit == 'T': return base**4 elif unit == 'P': return base**5 return 1 def split_unit(unit: str) -> Tuple[str, float]: if unit.endswith('B'): scale = get_scale(unit[:-1], 1024) return ('bytes', scale) elif unit.endswith('B/s'): scale = get_scale(unit[:-3], 1024) return ('bytes/s', scale) elif unit.endswith('s'): scale = get_scale(unit[:-1]) return ('s', scale) return ('', 1.) def normalize_units(row: Dict[str, Any], units: Dict[str, str]): normalized = {} for k, v in row.items(): suffix, scale = split_unit(units[k]) if suffix or scale != 1: k = k + ' ' + suffix if v: v = float(v) * scale else: v = None normalized[k] = v return normalized nvprofoutput_raw = nvprof_output_file.read() nvprof_statistics = {} try: # First line should look something like # ==[PID]== NVPROF is profiling process [PID], command: [some command] # Let's parse the PID part and use ==PID== as a comment marker in the future first_line = nvprofoutput_raw.split('\n', 1)[0] m = re.match(r'^==(\d+)== (.*)', first_line) pid = m.group(1) section = m.group(2) r = re.compile(fr'^=={re.escape(pid)}== (.*?)$', re.MULTILINE) pos = m.end() while True: m = r.search(nvprofoutput_raw, pos) if m is None: end = -1 else: end = m.start() content = nvprofoutput_raw[pos:end].strip() if section == 'Profiling result:': gpu_trace = list( csv.DictReader(io.StringIO(content), dialect=csv.unix_dialect)) # Normalize units units = gpu_trace[0] gpu_trace = gpu_trace[1:] if len(gpu_trace) <= 100: gpu_trace = [ normalize_units(row, units) for row in gpu_trace ] nvprof_statistics['gpu_trace'] = gpu_trace else: nvprof_statistics['gpu_trace'] = None nvprof_statistics[ 'gpu_trace_message'] = "Too long GPU trace." if m is None: break section = m.group(1) pos = m.end() except: # Parsing nvprof output failed pass if run_successful: output = os.fdopen(ppc_output_read, 'r').read() results = config.parse_output(output) else: os.close(ppc_output_read) results = [] return NvprofRunnerOutput(nvprofoutput_raw, nvprof_statistics, run_successful, timed_out, stdout, stderr, timeout, *results)
def add_omp_flags(self) -> 'Compiler': # Apple clang doesn't have openmp compiled so we want to include the homebrew package. if platform.system() == 'Darwin': try: brew_dir_command = ['brew', '--prefix'] log_command(brew_dir_command) brew_dir = subprocess.run(brew_dir_command, timeout=10, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8').stdout.strip() except FileNotFoundError: message = '''It seems that you don't have Homebrew installed. It is needed if you want to run OpenMP tasks on macOS. It would be great if you could install Homebrew from: https://brew.sh/ After that you need to install libomp with this command: brew install libomp ''' if platform.machine() != 'arm64': message += ''' Or alternatively you can try to install GCC and use it instead of Clang: brew install gcc ''' message += '''Once you have done that, please try to run the same command again, I should be able to find the right packages and compilers then!''' sys.exit(message) try: brew_libomp_command = ['brew', 'list', 'libomp'] log_command(brew_libomp_command) assert (subprocess.run(brew_libomp_command, timeout=10, stdout=subprocess.PIPE, stderr=subprocess.PIPE).returncode == 0) except subprocess.TimeoutExpired: print('Could not check required packages. Continuing...') except AssertionError: message = '''It seems that you have got Homebrew installed, which is great! However, it seems you do not have the libomp package installed. This is needed if you want to use OpenMP with the Clang C++ compiler. Could you please try to install libomp with this command: brew install libomp ''' if platform.machine() != 'arm64': message += ''' Or alternatively you can try to install GCC and use it instead of Clang: brew install gcc ''' message += '''Once you have done that, please try to run the same command again, I should be able to find the right packages and compilers then!''' sys.exit(message) self = self.add_flag('-Xpreprocessor', '-fopenmp') self = self.add_flag('-I', f'{brew_dir}/include') if sys.argv[1] != 'assembly': self = self.add_flag('-lomp') self = self.add_flag('-L', f'{brew_dir}/lib') else: self = self.add_flag('-fopenmp') return self
def run(self, config, args: List[str], timeout: Optional[float]) -> RunnerOutput: env = os.environ.copy() ppc_output_read, ppc_output_write = os.pipe() env['PPC_OUTPUT'] = str(ppc_output_write) env['PPC_PERF'] = 'default' # Run with nvprof nvprof_output_file = tempfile.NamedTemporaryFile('r') args = [ 'nvprof', '--csv', '--log-file', nvprof_output_file.name, '--normalized-time-unit', 's', '--print-gpu-trace', '--', ] + args log_command(args) process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL, env=env, encoding='utf-8', errors='utf-8', pass_fds=(ppc_output_write, )) os.close(ppc_output_write) try: stdout, stderr = process.communicate(None, timeout=timeout) run_successful = process.returncode == 0 timed_out = False except subprocess.TimeoutExpired: run_successful = False timed_out = True try: # Ask nicely to terminate before killing process.terminate() stdout, stderr = process.communicate(None, timeout=1) except subprocess.TimeoutExpired: process.kill() stdout, stderr = process.communicate(None) # Read nvprof output nvprofoutput_raw = nvprof_output_file.read() nvprof_statistics = {} try: # First line should look something like # ==[PID]== NVPROF is profiling process [PID], command: [some command] # Let's parse the PID part and use ==PID== as a comment marker in the future first_line = nvprofoutput_raw.split('\n', 1)[0] m = re.match(r'^==(\d+)== (.*)', first_line) pid = m.group(1) section = m.group(2) r = re.compile(fr'^=={re.escape(pid)}== (.*?)$', re.MULTILINE) pos = m.end() while True: m = r.search(nvprofoutput_raw, pos) if m is None: end = -1 else: end = m.start() content = nvprofoutput_raw[pos:end].strip() if section == 'Profiling result:': gpu_trace = list( csv.DictReader(io.StringIO(content), dialect=csv.unix_dialect)) # Skip units gpu_trace = gpu_trace[1:] nvprof_statistics['gpu_trace'] = gpu_trace if m is None: break section = m.group(1) pos = m.end() except: # Parsing nvprof output failed pass if run_successful: output = os.fdopen(ppc_output_read, 'r').read() results = config.parse_output(output) else: os.close(ppc_output_read) results = [] return NvprofRunnerOutput(nvprofoutput_raw, nvprof_statistics, run_successful, timed_out, stdout, stderr, timeout, *results)