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.

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

mercurial