testing/mozbase/mozprocess/tests/proclaunch.py

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 #!/usr/bin/env python
     3 import argparse
     4 import collections
     5 import ConfigParser
     6 import multiprocessing
     7 import time
     9 ProcessNode = collections.namedtuple('ProcessNode', ['maxtime', 'children'])
    11 class ProcessLauncher(object):
    13     """ Create and Launch process trees specified by a '.ini' file
    15         Typical .ini file accepted by this class :
    17         [main]
    18         children=c1, 1*c2, 4*c3
    19         maxtime=10
    21         [c1]
    22         children= 2*c2, c3
    23         maxtime=20
    25         [c2]
    26         children=3*c3
    27         maxtime=5
    29         [c3]
    30         maxtime=3
    32         This generates a process tree of the form:
    33             [main]
    34                 |---[c1]
    35                 |     |---[c2]
    36                 |     |     |---[c3]
    37                 |     |     |---[c3]
    38                 |     |     |---[c3]
    39                 |     |
    40                 |     |---[c2]
    41                 |     |     |---[c3]
    42                 |     |     |---[c3]
    43                 |     |     |---[c3]
    44                 |     |
    45                 |     |---[c3]
    46                 |
    47                 |---[c2]
    48                 |     |---[c3]
    49                 |     |---[c3]
    50                 |     |---[c3]
    51                 |
    52                 |---[c3]
    53                 |---[c3]
    54                 |---[c3]
    56         Caveat: The section names cannot contain a '*'(asterisk) or a ','(comma)
    57         character as these are used as delimiters for parsing.
    58     """
    60     # Unit time for processes in seconds
    61     UNIT_TIME = 1
    63     def __init__(self, manifest, verbose=False):
    64         """
    65         Parses the manifest and stores the information about the process tree
    66         in a format usable by the class.
    68         Raises IOError if :
    69             - The path does not exist
    70             - The file cannot be read
    71         Raises ConfigParser.*Error if:
    72             - Files does not contain section headers
    73             - File cannot be parsed because of incorrect specification
    75         :param manifest: Path to the manifest file that contains the
    76         configuration for the process tree to be launched
    77         :verbose: Print the process start and end information.
    78         Genrates a lot of output. Disabled by default.
    79         """
    81         self.verbose=verbose
    83         # Children is a dictionary used to store information from the,
    84         # Configuration file in a more usable format.
    85         # Key : string contain the name of child process
    86         # Value : A Named tuple of the form (max_time, (list of child processes of Key))
    87         #   Where each child process is a list of type: [count to run, name of child]
    88         self.children = {}
    91         cfgparser = ConfigParser.ConfigParser()
    93         if not cfgparser.read(manifest):
    94             raise IOError('The manifest %s could not be found/opened', manifest)
    96         sections = cfgparser.sections()
    97         for section in sections:
    98             # Maxtime is a mandatory option
    99             # ConfigParser.NoOptionError is raised if maxtime does not exist
   100             if '*' in section or ',' in section:
   101                 raise ConfigParser.ParsingError('%s is not a valid section name. Section names cannot contain a \'*\' or \',\'.' % section)
   102             m_time = cfgparser.get(section, 'maxtime')
   103             try:
   104                 m_time = int(m_time)
   105             except ValueError:
   106                 raise ValueError('Expected maxtime to be an integer, specified %s' % m_time)
   108             # No children option implies there are no further children
   109             # Leaving the children option blank is an error.
   110             try:
   111                 c = cfgparser.get(section, 'children')
   112                 if not c:
   113                     # If children is an empty field, assume no children
   114                     children = None
   116                 else:
   117                     # Tokenize chilren field, ignore empty strings
   118                     children = [[y.strip() for y in x.strip().split('*', 1)]
   119                                 for x in c.split(',') if x]
   120                     try:
   121                         for i, child in enumerate(children):
   122                             # No multiplicate factor infront of a process implies 1
   123                             if len(child) == 1:
   124                                 children[i] = [1, child[0]]
   125                             else:
   126                                 children[i][0] = int(child[0])
   128                             if children[i][1] not in sections:
   129                                 raise ConfigParser.ParsingError('No section corresponding to child %s' % child[1])
   130                     except ValueError:
   131                         raise ValueError('Expected process count to be an integer, specified %s' % child[0])
   133             except ConfigParser.NoOptionError:
   134                 children = None
   135             pn = ProcessNode(maxtime=m_time,
   136                              children=children)
   137             self.children[section] = pn
   139     def run(self):
   140         """
   141         This function launches the process tree.
   142         """
   143         self._run('main', 0)
   145     def _run(self, proc_name, level):
   146         """
   147         Runs the process specified by the section-name `proc_name` in the manifest file.
   148         Then makes calls to launch the child processes of `proc_name`
   150         :param proc_name: File name of the manifest as a string.
   151         :param level: Depth of the current process in the tree.
   152         """
   153         if proc_name not in self.children.keys():
   154             raise IOError("%s is not a valid process" % proc_name)
   156         maxtime = self.children[proc_name].maxtime
   157         if self.verbose:
   158             print "%sLaunching %s for %d*%d seconds" % (" "*level, proc_name, maxtime, self.UNIT_TIME)
   160         while self.children[proc_name].children:
   161             child = self.children[proc_name].children.pop()
   163             count, child_proc = child
   164             for i in range(count):
   165                 p = multiprocessing.Process(target=self._run, args=(child[1], level+1))
   166                 p.start()
   168         self._launch(maxtime)
   169         if self.verbose:
   170             print "%sFinished %s" % (" "*level, proc_name)
   172     def _launch(self, running_time):
   173         """
   174         Create and launch a process and idles for the time specified by
   175         `running_time`
   177         :param running_time: Running time of the process in seconds.
   178         """
   179         elapsed_time = 0
   181         while elapsed_time < running_time:
   182             time.sleep(self.UNIT_TIME)
   183             elapsed_time += self.UNIT_TIME
   185 if __name__ == '__main__':
   187     parser = argparse.ArgumentParser()
   188     parser.add_argument("manifest", help="Specify the configuration .ini file")
   189     args = parser.parse_args()
   191     proclaunch = ProcessLauncher(args.manifest)
   192     proclaunch.run()

mercurial