1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3 4#------------------------------------------------------------------------------- 5 6# This file is part of Code_Saturne, a general-purpose CFD tool. 7# 8# Copyright (C) 1998-2021 EDF S.A. 9# 10# This program is free software; you can redistribute it and/or modify it under 11# the terms of the GNU General Public License as published by the Free Software 12# Foundation; either version 2 of the License, or (at your option) any later 13# version. 14# 15# This program is distributed in the hope that it will be useful, but WITHOUT 16# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 17# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 18# details. 19# 20# You should have received a copy of the GNU General Public License along with 21# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 22# Street, Fifth Floor, Boston, MA 02110-1301, USA. 23 24#------------------------------------------------------------------------------- 25 26""" 27This module defines a wrapper to launch an executable under a debugger. 28""" 29 30#=============================================================================== 31# Import required Python modules 32#=============================================================================== 33 34import sys, os, stat 35import subprocess 36 37#------------------------------------------------------------------------------- 38# Global state variables 39#------------------------------------------------------------------------------- 40 41# MPI rank 42 43rank_id = -1 44 45# List of supported debuggers 46 47debuggers = {"gdb": "GNU gdb debugger", 48 "cgdb": "Console front-end to gdb", 49 "cuda-gdb": "CUDA debugger", 50 "gdbgui": "gdbgui gdb web browser interface", 51 "ddd": "Data Display Debugger", 52 "emacs": "Emacs with gdb debugger", 53 "kdbg": "KDbg", 54 "kdevelop": "Kdevelop", 55 "gede": "Gede", 56 "nemiver": "Nemiver"} 57 58#------------------------------------------------------------------------------- 59# Enquote arguments if required 60#------------------------------------------------------------------------------- 61 62def enquote_arg(s): 63 """ 64 Add quotes around argument if it contains whitespace, leave it 65 unchanged otherwise; if the argument already contains unprotected 66 quotes, do not add any more (so for example --option="string 1" 67 is unchanged). 68 """ 69 70 if s: 71 if (s.find(' ') > -1): 72 protect = False 73 for i in range(len(s)): 74 if s[i] == '\\': 75 protect = not protect 76 if not protect and s[i] == '"': 77 return s 78 return '"' + s + '"' 79 else: 80 return s 81 else: 82 return s 83 84#------------------------------------------------------------------------------- 85# Print a help page. 86#------------------------------------------------------------------------------- 87 88def print_help(): 89 """ 90 Print a help page. 91 """ 92 93 help_string = \ 94""" 95This is a debugger launcher wrapper. Usage: 96 97%s [debugger opts] [mpiexec opts] [valgrind opts] --program=<program> [arguments] 98 99This wrapper may be run either under MPI, or include an mpi run command. 100If no debugger or Valgrind options are specified, the program is run under 101the gdb debugger. 102 103Debugger options: 104 105 --debugger Indicates the program should be debugged 106 --debugger=DEBUGGER Allows selection of the debugger 107 DEBUGGER Same as above (if debugger in known list) 108 --debugger=list Lists debuggers supported by this script 109 --asan-bp Adds a breakpoint for gcc's Address-Sanitizer 110 --back-end=GDB Path to debugger back-end (for graphical front-ends) 111 --breakpoints=LIST Comma-separated list of breakpoints to insert 112 --terminal=TERM Select terminal type to use for console debugger 113 114 Other, standard options specific to each debugger may also be 115 used, as long as they do not conflict with options in this script. 116 117MPI options: 118 119 If it recognizes mpi execution commands, this launcher will re-run 120 itself under MPI, using the provided options. This allows simply 121 prefixing debug options to a command-line, and may allow for 122 support of parallel debuggers. 123 124Valgrind options: 125 126 --valgrind Indicates the program should be run under Valgrind 127 --valgrind=VALGRIND Allows selection of the valgrind path 128 VALGRIND Same as above 129 130 Other valgrind options may be used. Most importantly, when the 131 --vgdb-errors=<num> option is used, the progam is run under a Valgrind 132 gdb server, and debugged with the specified gdb debugger interface. 133 When no debugger option is provided, or the (deprecated) 134 --db-attach=yes option is used, the program is run under Valgrind, 135 possibly under separate terminals when run under MPI. 136 137Program options: 138 139 --program=PROGRAM Specifies the program that should be debugged 140 PROGRAM Same as above 141 142 Program arguments should follow the program name or path. 143 144""" 145 print(help_string % sys.argv[0]) 146 147#------------------------------------------------------------------------------- 148# Process the command line arguments 149#------------------------------------------------------------------------------- 150 151def process_cmd_line(argv, pkg): 152 """ 153 Process the passed command line arguments. 154 """ 155 156 positions = {"debugger": -1, 157 "valgrind": -1, 158 "mpiexec": -1, 159 "program": -1} 160 161 debugger_options = ("asan-bp", "back-end", "breakpoints", "terminal") 162 163 files = [] 164 165 p = ['.'] + os.getenv('PATH').split(':') 166 167 # First loop on options to determine option groups and executables 168 169 idx = 0 170 for a in argv: 171 172 ie = a[2:].find("=") 173 if ie > -1: 174 b = a[2:ie+2] 175 else: 176 b = a[2:] 177 if b in positions: 178 if positions[b] < 0 or positions[b] > idx: 179 positions[b] = idx 180 if a == '--debugger=list': 181 for k in debuggers.keys(): 182 print(k + ': ' + debuggers[k]) 183 return None 184 185 if not a[0] == '-': 186 # Check for executable files 187 rname = None 188 ename = os.path.expandvars(os.path.expanduser(a)) 189 sname = os.path.split(ename) 190 if os.path.isabs(ename) or (len(sname) > 1 and sname[0]): 191 rname = os.path.realpath(ename) 192 else: 193 for d in p: 194 absname = os.path.join(d, ename) 195 if os.path.isfile(absname) or os.path.islink(absname): 196 rname = os.path.realpath(absname) 197 break 198 if rname: 199 if os.path.isfile(rname): 200 files.append((a, idx)) 201 202 idx += 1 203 204 # Check if mpiexec or equivalent is inside the command, so 205 # as to place it first (we want to debug the application, 206 # not mpiexec). Also check in case Valgrind is passed 207 # directly as a path, not --valgrind. 208 209 for f in files: 210 b = os.path.basename(f[0]).split('.')[0] 211 if b in ['mpiexec', 'mpirun', 'srun', 'aprun', 'poe']: 212 positions['mpiexec'] = f[1] 213 elif b in debuggers.keys(): 214 positions['debugger'] = f[1] 215 elif b == 'valgrind' and positions['valgrind'] == -1: 216 positions['valgrind'] = f[1] 217 218 # Now check for executable: 219 220 if positions['program'] == -1: 221 s_id = -1 222 for k in ['mpiexec', 'debugger', 'valgrind']: 223 if positions[k] > s_id: 224 s_id = positions[k] 225 for f in files: 226 if f[1] > s_id and positions['program'] == -1: 227 p = os.path.realpath(f[0]) 228 if os.path.isfile(p) or os.path.islink(p): 229 if os.stat(p).st_mode & stat.S_IXUSR: 230 positions['program'] = f[1] 231 232 # Second check if Valgrind is used, based on the fact that all 233 # Valgrind options start with "-". 234 235 if positions['program'] == -1 and positions['valgrind'] > -1: 236 i = positions['valgrind'] + 1 237 for p in argv[i:]: 238 if p[0] != '-': 239 positions['program'] = i 240 break 241 else: 242 i += 1 243 244 # Check for error 245 246 if positions['program'] == -1: 247 for a in argv: 248 if a == '-h' or a == '--help': 249 print_help() 250 return None 251 print("usage: %s [debugger opts] [mpiexec opts] [valgrind opts] " 252 % sys.argv[0] + "--program=<program> [arguments]") 253 return None 254 255 # Now get all parts 256 257 o_idx = list(positions.values()) 258 o_idx.sort() 259 260 cmds = {} 261 for s in positions.keys(): 262 if positions[s] > -1: 263 s_idx = positions[s] 264 e_idx = len(argv) 265 for p in o_idx: 266 if p > s_idx and p < e_idx: 267 e_idx = p 268 # Unify --<tool>=<path> with <path> logic 269 tool = argv[s_idx] 270 ie = tool[2:].find("=") 271 if tool[0] == '-' and ie > -1: 272 tool = tool[ie+3:] 273 cmds[s] = [tool] + argv[s_idx+1:e_idx] 274 275 # Debugger options 276 277 if positions['debugger'] == -1: 278 need_gdb = True 279 if 'valgrind' in cmds.keys(): 280 need_gdb = False 281 for k in cmds['valgrind'][1:]: 282 if k[0:6] == '--vgdb': 283 need_gdb = True 284 for k in cmds['valgrind'][1:]: 285 if k == '--vgdb=off': 286 need_gdb = False 287 if need_gdb: 288 debugger = 'gdb' 289 p_min = len(argv) 290 for s in positions.keys(): 291 if positions[s] > -1 and positions[s] < p_min: 292 p_min = positions[s] 293 cmds['debugger'] = [debugger] + argv[0:p_min] 294 295 # For for debugger options which might have appeared first 296 # check only now to avoid minor risk of similarly-named options 297 # for other tools 298 299 idx_s = 0 300 idx_e = -1 301 for k in ('debugger', 'mpiexec', 'valgrind', 'program'): 302 if positions[k] > -1: 303 idx_e = positions[k] 304 break; 305 306 idx_mpi = positions['mpiexec'] # in case placed first in the future 307 if idx_mpi > -1 and idx_mpi < idx_e: 308 idx_e = idx_mpi 309 310 idx = 0 311 for idx in range(idx_s, idx_e): 312 a = argv[idx] 313 ie = a[2:].find("=") 314 if ie > -1: 315 b = a[2:ie+2] 316 else: 317 b = a[2:] 318 319 if b in debugger_options: 320 idx_s = idx 321 break 322 323 try: 324 if cmds['debugger']: 325 idx_insert = 1 326 for idx in range(idx_s, idx_e): 327 if argv[idx] not in cmds['debugger']: 328 cmds['debugger'].insert(idx_insert, argv[idx]) 329 idx_insert += 1 330 except Exception: 331 pass 332 333 return cmds 334 335#------------------------------------------------------------------------------- 336# Determine rank id when run under MPI 337#------------------------------------------------------------------------------- 338 339def init_rank_id(): 340 """ 341 Determine rank id when run under MPI. 342 """ 343 344 rank_env_vars = ['PMI_RANK', 'OMPI_COMM_WORLD_RANK', 'PMI_ID', 345 'SLURM_PROCID', 'MPI_RANKID', 'MP_CHILD', 'MPIRUN_RANK'] 346 347 global rank_id 348 for v in rank_env_vars: 349 if os.getenv(v): 350 rank_id = int(os.getenv(v)) 351 break 352 353#------------------------------------------------------------------------------- 354# Generate a gdb command file. 355#------------------------------------------------------------------------------- 356 357def gen_cmd_file(cmds): 358 """ 359 Generate a gdb command file. 360 """ 361 362 # Build file name 363 364 if rank_id > -1: 365 f_name = "./commands_r" + str(rank_id) + ".gdb" 366 else: 367 f_name = "./commands.gdb" 368 f = open(f_name, "w") 369 f.write("set breakpoint pending on\n") 370 for c in cmds: 371 f.write(c + '\n') 372 f.close() 373 374 return f_name 375 376#------------------------------------------------------------------------------- 377# Run gdb-based or compatible debugger with minimal options 378#------------------------------------------------------------------------------- 379 380def run_minimal_debug(path, args=None, 381 debugger='gdb', debugger_opts=None, 382 debugger_ui='terminal'): 383 """ 384 Run gdb-based or compatible debugger with minimal options. 385 This is useful for debuggers such as KDevelop and Nemiver, whose 386 command lines do not allow passing GDB options or command files. 387 """ 388 389 # Start building debugger command options 390 391 cmd = [debugger] 392 393 gdb = 'gdb' 394 if debugger_opts: 395 for o in debugger_opts: 396 if o.find('--back-end=') == 0: # Specify back-end 397 gdb = o[o.find('=')+1:] 398 399 if debugger_ui == 'kdevelop': 400 cmd += ['--debug', gdb] 401 cmd += [path] 402 if args: 403 cmd += args 404 405 elif debugger_ui == 'kdbg': 406 if args: 407 cmd += ['-a'] + args 408 cmd += [path] 409 410 elif debugger_ui == 'gede': 411 cmd += ['--args', path] 412 if args: 413 cmd += args 414 415 elif debugger_ui == 'nemiver': 416 if gdb != 'gdb': 417 cmd += ['--gdb-binary=' + gdb] 418 cmd += [path] 419 if args: 420 cmd += args 421 422 else: 423 cmd += [path] 424 if args: 425 cmd += args 426 427 return subprocess.call(cmd) 428 429#------------------------------------------------------------------------------- 430# Find available port (for gdbgui) 431#------------------------------------------------------------------------------- 432 433def find_free_port(): 434 import socket 435 tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 436 tcp.bind(('', 0)) 437 addr, port = tcp.getsockname() 438 tcp.close() 439 return port 440 441#------------------------------------------------------------------------------- 442# Run gdb-based or compatible debugger. 443#------------------------------------------------------------------------------- 444 445def run_gdb_debug(path, args=None, gdb_cmds=None, 446 debugger='gdb', debugger_opts=None, debugger_ui='terminal'): 447 """ 448 Run gdb-based or compatible debugger. 449 """ 450 451 # Combine given (gdb_cmds) and needed gdb commands in "cmds" list 452 453 cmds = [] 454 455 target = False 456 if gdb_cmds: 457 if gdb_cmds[0].find('target') == 0: 458 target = True 459 cmds.append(gdb_cmds[0]) 460 461 # Check if we already have a command file 462 463 cmds.append('b main') 464 465 if not target: 466 run_cmd = "run" 467 if args: 468 for a in args: 469 if a.find(' ') > -1: 470 run_cmd += ' "' + a + '"' 471 else: 472 run_cmd += ' ' + a 473 cmds.append(run_cmd) 474 475 if gdb_cmds: 476 for c in gdb_cmds: 477 if c.find('target') < 0: 478 cmds.append(c) 479 480 if target: 481 cmds.append('continue') 482 483 cmds.append('b exit') 484 485 # Start building debugger command options 486 487 cmd = [] 488 489 # Add debugger options, with a specific handling of 490 # existing debugger command files 491 492 gdb = 'gdb' 493 494 debugger_command_file = None 495 496 term = os.getenv('TERM') 497 if not term: 498 term = 'xterm' 499 elif term[:5] == 'xterm': 500 term = 'xterm' 501 502 if debugger_opts: 503 file_next = False 504 for o in debugger_opts: 505 if o == '-x': 506 file_next = True 507 elif file_next: 508 debugger_command_file = cmd 509 file_next = False 510 elif o.find('--back-end=') == 0: # Specify back-end 511 gdb = o[o.find('=')+1:] 512 elif o.find('--breakpoints=') == 0: # Specify breakpoints 513 for bp in o[o.find('=')+1:].split(','): 514 cmds.append('b ' + bp) 515 elif o == '--asan-bp': # gcc Adress sanitizer breakpoints 516 cmds.append('b __asan::ReportGenericError') 517 elif o.find('--terminal=') == 0: # Specify terminal 518 term = o[o.find('=')+1:] 519 else: 520 cmd.append(o) 521 522 f = gen_cmd_file(cmds) 523 524 if debugger_ui == 'gdbgui': 525 cmd.append('--gdb-cmd') 526 cmd.append(gdb + ' -x ' + f + ' ' + path) 527 else: 528 cmd.append('-x') 529 cmd.append(f) 530 531 if debugger_command_file: 532 fp = open(debugger_command_file) 533 lines = f.readlines() 534 fp.close() 535 fp = open(f, 'a') 536 for l in lines: 537 fp.write(l) 538 fp.close() 539 540 # Finalize command 541 542 if debugger_ui != 'gdbgui': 543 cmd.append(path) 544 545 # Start building command to run 546 547 if debugger_ui in ['terminal', 'cgdb']: 548 cmd_string = str(debugger) 549 for c in cmd: 550 cmd_string += ' ' + enquote_arg(str(c)) 551 cmd = [term, '-e', cmd_string] 552 553 elif debugger_ui == 'gdbgui': 554 cmd.insert(0, debugger) 555 if gdb != 'gdb': 556 cmd.insert(1, gdb) 557 cmd.insert(1, '--gdb') 558 p = find_free_port() 559 cmd.insert(1, str(p)) 560 cmd.insert(1, '-p') 561 562 elif debugger_ui == 'ddd': 563 cmd.insert(0, debugger) 564 if gdb != 'gdb': 565 cmd.insert(1, gdb) 566 cmd.insert(1, '--debugger') 567 568 elif debugger_ui == 'emacs': 569 cmd_string = r'"(gdb \"' + gdb + ' -i=mi' # emacs 24 and newer 570 for c in cmd: 571 if cmd != gdb: 572 cmd_string += ' ' + enquote_arg(str(c)) 573 cmd_string += r'\")"' 574 cmd = [debugger, '--eval', cmd_string] 575 576 if not cmd[0]: 577 cmd[0] = 'gdb' 578 579 if not debugger_ui in ("emacs"): 580 print(cmd) 581 p = subprocess.Popen(cmd) 582 else: 583 cmd_line = cmd[0] 584 for c in cmd[1:]: 585 cmd_line += ' ' + c 586 p = subprocess.Popen(cmd_line, shell=True) 587 588 return p 589 590#------------------------------------------------------------------------------- 591# Run Valgrind gdb server and debugger. 592#------------------------------------------------------------------------------- 593 594def run_vgdb_debug(path, 595 args = None, 596 valgrind = 'valgrind', 597 valgrind_opts = None, 598 debugger = 'gdb', 599 debugger_opts=None, 600 debugger_ui = 'terminal'): 601 """ 602 Run Valgrind gdb server and debugger. 603 """ 604 605 kwargs = {} 606 kwargs['stderr'] = subprocess.PIPE 607 608 cmds = [] 609 610 cmd = [valgrind] 611 if valgrind_opts: 612 cmd += valgrind_opts 613 cmd += [path] 614 if args: 615 cmd += args 616 p0 = subprocess.Popen(cmd, universal_newlines=True, **kwargs) 617 618 cmd = None 619 p1 = None 620 621 vgdb_pid = None 622 623 while p0.poll() == None: 624 output = p0.stderr.readline() 625 if not cmd: 626 idx = output.find("target remote") 627 if idx > -1: 628 cmd = output[idx:].rstrip() 629 # Work around bug on some Debian 10 systems 630 c = cmd.split() 631 if not os.path.isfile(c[3]): 632 if c[3][:9] == '/usr/lib/': 633 c[3] = '/usr/bin/vgdb' 634 if os.path.isfile(c[3]): 635 cmd = ' '.join(c) 636 try: 637 vgdb_pid = int(cmd[cmd.index("--pid")+6:]) 638 except Exception: 639 pass 640 cmds.insert(0, cmd) 641 p1 = run_gdb_debug(path, args, cmds, 642 debugger, debugger_opts, 643 debugger_ui) 644 print(output.rstrip()) 645 if p1: 646 if p1.poll() != None: 647 break 648 649 if p1: 650 if p1.returncode != None: # Debugger has finished/exited 651 p0.poll() 652 if p0.returncode == None: 653 if vgdb_pid: 654 # make sure vgdb is from the same path as Valgrind 655 if os.path.isabs(valgrind): 656 vgdb = os.path.join(os.path.dirname(valgrind), "vgdb") 657 else: 658 vgdb = "vgdb" 659 subprocess.call([vgdb, "--pid="+str(vgdb_pid), "v.kill"]) 660 else: 661 p0.kill() 662 663 p0.communicate() 664 665 if p1: 666 p1.communicate() 667 668 return p0.returncode 669 670#------------------------------------------------------------------------------- 671# Run program under Valgrind. 672#------------------------------------------------------------------------------- 673 674def run_valgrind(path, args=None, valgrind='valgrind', valgrind_opts=None, 675 debugger_opts=None): 676 """ 677 Run program under Valgrind. 678 """ 679 680 # Start building valgrind command options 681 682 cmd = [valgrind] 683 684 # Add valgrind options, with a specific handling of 685 # existing debugger command files 686 687 debugger_attach = False 688 689 if valgrind_opts: 690 cmd += valgrind_opts 691 for o in valgrind_opts: 692 if o[0:5] == '--db-': 693 debugger_attach = True 694 break 695 696 cmd += [path] 697 698 if args: 699 cmd += args 700 701 # Start building command to run 702 703 if debugger_attach: 704 if rank_id < 0: 705 return subprocess.call(cmd) 706 else: 707 term = os.getenv('TERM') 708 if not term: 709 term = xterm 710 if debugger_opts: 711 for o in debugger_opts: 712 if o.find('--terminal=') == 0: # Specify terminal 713 term = o[o.find('=')+1:] 714 715 cmd_line = term + ' -e "' + valgrind 716 for c in cmd[1:]: 717 cmd_line += ' ' + enquote_arg(str(c)) 718 cmd_line += ' ; read"' 719 720 return subprocess.call(cmd_line, shell=True) 721 722 else: 723 if rank_id > 0: 724 vg_f = open('valgrind.out.r' + str(rank_id), 'w') 725 returncode = subprocess.call(cmd, 726 stdout=vg_f, 727 stderr=subprocess.STDOUT) 728 vg_f.close() 729 return returncode 730 else: 731 return subprocess.call(cmd) 732 733#------------------------------------------------------------------------------- 734# Run debugger 735#------------------------------------------------------------------------------- 736 737def run_debug(cmds): 738 """ 739 Run debugger. 740 """ 741 742 # Initializations 743 744 cmd = [] 745 746 debugger_type = 'gdb' 747 vgdb = False 748 need_terminal = False 749 750 # Tests for Valgrind 751 752 valgrind = None 753 754 if 'valgrind' in cmds.keys(): 755 756 valgrind = cmds['valgrind'][0] 757 for cmd in cmds['valgrind'][1:]: 758 if cmd[0:6] == '--vgdb': 759 vgdb = True 760 else: 761 if cmd.find("--db-attach") > -1: 762 need_terminal = True 763 for cmd in cmds['valgrind'][1:]: 764 if cmd == '--vgdb=off': 765 vgdb = False 766 767 # Tests for debugger choice 768 769 debugger_ui = 'terminal' 770 771 if 'debugger' in cmds.keys(): 772 debugger = cmds['debugger'][0] 773 dbg_name = os.path.basename(debugger) 774 if dbg_name in debuggers.keys() and dbg_name not in ('gdb', 'cuda-gdb'): 775 debugger_ui = dbg_name 776 elif dbg_name.find("emacs") > -1: 777 debugger_ui = 'emacs' 778 commands = None 779 for cmd in cmds['debugger'][1:]: 780 if debugger_type == 'gdb' and cmd == '--tui': 781 need_terminal = True 782 783 if need_terminal: 784 debugger_ui = 'terminal' 785 786 # Now run appropriate tool: 787 788 if vgdb: 789 return run_vgdb_debug(path = cmds['program'][0], 790 args = cmds['program'][1:], 791 valgrind = cmds['valgrind'][0], 792 valgrind_opts = cmds['valgrind'][1:], 793 debugger = cmds['debugger'][0], 794 debugger_opts = cmds['debugger'][1:], 795 debugger_ui = debugger_ui) 796 797 elif 'debugger' in cmds.keys(): 798 if debugger in ['kdbg', 'kdevelop', 'gede', 'nemiver']: 799 return run_minimal_debug(path = cmds['program'][0], 800 args = cmds['program'][1:], 801 debugger = cmds['debugger'][0], 802 debugger_opts = cmds['debugger'][1:], 803 debugger_ui = debugger_ui) 804 else: 805 p = run_gdb_debug(path = cmds['program'][0], 806 args = cmds['program'][1:], 807 gdb_cmds = None, 808 debugger = cmds['debugger'][0], 809 debugger_opts = cmds['debugger'][1:], 810 debugger_ui = debugger_ui) 811 p.communicate() 812 return p.returncode 813 814 elif 'valgrind' in cmds.keys(): 815 debugger_opts = None 816 if 'debugger' in cmds.keys(): 817 debugger_opts = cmds['debugger'][1:] 818 return run_valgrind(path = cmds['program'][0], 819 args = cmds['program'][1:], 820 valgrind = cmds['valgrind'][0], 821 valgrind_opts = cmds['valgrind'][1:], 822 debugger_opts = debugger_opts) 823 824 # We should not reach this area. 825 826 else: 827 return 1 828 829#=============================================================================== 830# Run the calculation 831#=============================================================================== 832 833def main(argv, pkg=None): 834 """ 835 Main function. 836 """ 837 838 cmds = process_cmd_line(argv, pkg) 839 840 if not cmds: 841 return 1 842 843 # When command includes mpiexec, re-run script under MPI 844 845 if 'mpiexec' in cmds.keys(): 846 cmd_mpi = cmds['mpiexec'] 847 cmd_mpi.append(sys.argv[0]) 848 for k in ['debugger', 'valgrind', 'program']: 849 if k in cmds: 850 cmd_mpi += cmds[k] 851 return subprocess.call(cmd_mpi) 852 853 else: 854 init_rank_id() 855 return run_debug(cmds) 856 857#------------------------------------------------------------------------------- 858 859if __name__ == '__main__': 860 861 retval = main(sys.argv[1:]) 862 863 sys.exit(retval) 864 865#------------------------------------------------------------------------------- 866# End 867#------------------------------------------------------------------------------- 868