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