diff options
| author | Paul Eggleton <paul.eggleton@linux.intel.com> | 2016-06-23 22:59:06 +1200 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-07-08 09:57:26 +0100 |
| commit | 0e3281f68b511ab7bf9d6b1c1f905ec794d3032a (patch) | |
| tree | 3204dcb4d2eb439a3d8559d160aeb8711ef68f26 | |
| parent | ac5e720575dd3c8f86514a7a646de82c8cc28e17 (diff) | |
| download | poky-0e3281f68b511ab7bf9d6b1c1f905ec794d3032a.tar.gz | |
bitbake: lib/bb/progress: add MultiStageProgressReporter
Add a class to help report progress in a task that consists of multiple
stages, some of which may have internal progress (do_rootfs within
OpenEmbedded is one example). Each stage is weighted to try to give
a reasonable representation of progress over time.
Part of the implementation for [YOCTO #5383].
(Bitbake rev: 751b75602872a89e8b1a7c03269bc0fdaa149c6f)
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
| -rw-r--r-- | bitbake/lib/bb/progress.py | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/bitbake/lib/bb/progress.py b/bitbake/lib/bb/progress.py index bab8e9465d..93e42dfcdb 100644 --- a/bitbake/lib/bb/progress.py +++ b/bitbake/lib/bb/progress.py | |||
| @@ -20,6 +20,7 @@ BitBake progress handling code | |||
| 20 | import sys | 20 | import sys |
| 21 | import re | 21 | import re |
| 22 | import time | 22 | import time |
| 23 | import inspect | ||
| 23 | import bb.event | 24 | import bb.event |
| 24 | import bb.build | 25 | import bb.build |
| 25 | 26 | ||
| @@ -84,3 +85,113 @@ class OutOfProgressHandler(ProgressHandler): | |||
| 84 | progress = (float(nums[-1][0]) / float(nums[-1][1])) * 100 | 85 | progress = (float(nums[-1][0]) / float(nums[-1][1])) * 100 |
| 85 | self.update(progress) | 86 | self.update(progress) |
| 86 | super(OutOfProgressHandler, self).write(string) | 87 | super(OutOfProgressHandler, self).write(string) |
| 88 | |||
| 89 | class MultiStageProgressReporter(object): | ||
| 90 | """ | ||
| 91 | Class which allows reporting progress without the caller | ||
| 92 | having to know where they are in the overall sequence. Useful | ||
| 93 | for tasks made up of python code spread across multiple | ||
| 94 | classes / functions - the progress reporter object can | ||
| 95 | be passed around or stored at the object level and calls | ||
| 96 | to next_stage() and update() made whereever needed. | ||
| 97 | """ | ||
| 98 | def __init__(self, d, stage_weights, debug=False): | ||
| 99 | """ | ||
| 100 | Initialise the progress reporter. | ||
| 101 | |||
| 102 | Parameters: | ||
| 103 | * d: the datastore (needed for firing the events) | ||
| 104 | * stage_weights: a list of weight values, one for each stage. | ||
| 105 | The value is scaled internally so you only need to specify | ||
| 106 | values relative to other values in the list, so if there | ||
| 107 | are two stages and the first takes 2s and the second takes | ||
| 108 | 10s you would specify [2, 10] (or [1, 5], it doesn't matter). | ||
| 109 | * debug: specify True (and ensure you call finish() at the end) | ||
| 110 | in order to show a printout of the calculated stage weights | ||
| 111 | based on timing each stage. Use this to determine what the | ||
| 112 | weights should be when you're not sure. | ||
| 113 | """ | ||
| 114 | self._data = d | ||
| 115 | total = sum(stage_weights) | ||
| 116 | self._stage_weights = [float(x)/total for x in stage_weights] | ||
| 117 | self._stage = -1 | ||
| 118 | self._base_progress = 0 | ||
| 119 | # Send an initial progress event so the bar gets shown | ||
| 120 | self._fire_progress(0) | ||
| 121 | self._debug = debug | ||
| 122 | self._finished = False | ||
| 123 | if self._debug: | ||
| 124 | self._last_time = time.time() | ||
| 125 | self._stage_times = [] | ||
| 126 | self._stage_total = None | ||
| 127 | self._callers = [] | ||
| 128 | |||
| 129 | def _fire_progress(self, taskprogress): | ||
| 130 | bb.event.fire(bb.build.TaskProgress(taskprogress), self._data) | ||
| 131 | |||
| 132 | def next_stage(self, stage_total=None): | ||
| 133 | """ | ||
| 134 | Move to the next stage. | ||
| 135 | Parameters: | ||
| 136 | * stage_total: optional total for progress within the stage, | ||
| 137 | see update() for details | ||
| 138 | NOTE: you need to call this before the first stage. | ||
| 139 | """ | ||
| 140 | self._stage += 1 | ||
| 141 | self._stage_total = stage_total | ||
| 142 | if self._stage == 0: | ||
| 143 | # First stage | ||
| 144 | if self._debug: | ||
| 145 | self._last_time = time.time() | ||
| 146 | else: | ||
| 147 | if self._stage < len(self._stage_weights): | ||
| 148 | self._base_progress = sum(self._stage_weights[:self._stage]) * 100 | ||
| 149 | if self._debug: | ||
| 150 | currtime = time.time() | ||
| 151 | self._stage_times.append(currtime - self._last_time) | ||
| 152 | self._last_time = currtime | ||
| 153 | self._callers.append(inspect.getouterframes(inspect.currentframe())[1]) | ||
| 154 | elif not self._debug: | ||
| 155 | bb.warn('ProgressReporter: current stage beyond declared number of stages') | ||
| 156 | self._base_progress = 100 | ||
| 157 | self._fire_progress(self._base_progress) | ||
| 158 | |||
| 159 | def update(self, stage_progress): | ||
| 160 | """ | ||
| 161 | Update progress within the current stage. | ||
| 162 | Parameters: | ||
| 163 | * stage_progress: progress value within the stage. If stage_total | ||
| 164 | was specified when next_stage() was last called, then this | ||
| 165 | value is considered to be out of stage_total, otherwise it should | ||
| 166 | be a percentage value from 0 to 100. | ||
| 167 | """ | ||
| 168 | if self._stage_total: | ||
| 169 | stage_progress = (float(stage_progress) / self._stage_total) * 100 | ||
| 170 | if self._stage < 0: | ||
| 171 | bb.warn('ProgressReporter: update called before first call to next_stage()') | ||
| 172 | elif self._stage < len(self._stage_weights): | ||
| 173 | progress = self._base_progress + (stage_progress * self._stage_weights[self._stage]) | ||
| 174 | else: | ||
| 175 | progress = self._base_progress | ||
| 176 | if progress > 100: | ||
| 177 | progress = 100 | ||
| 178 | self._fire_progress(progress) | ||
| 179 | |||
| 180 | def finish(self): | ||
| 181 | if self._finished: | ||
| 182 | return | ||
| 183 | self._finished = True | ||
| 184 | if self._debug: | ||
| 185 | import math | ||
| 186 | self._stage_times.append(time.time() - self._last_time) | ||
| 187 | mintime = max(min(self._stage_times), 0.01) | ||
| 188 | self._callers.append(None) | ||
| 189 | stage_weights = [int(math.ceil(x / mintime)) for x in self._stage_times] | ||
| 190 | bb.warn('Stage weights: %s' % stage_weights) | ||
| 191 | out = [] | ||
| 192 | for stage_weight, caller in zip(stage_weights, self._callers): | ||
| 193 | if caller: | ||
| 194 | out.append('Up to %s:%d: %d' % (caller[1], caller[2], stage_weight)) | ||
| 195 | else: | ||
| 196 | out.append('Up to finish: %d' % stage_weight) | ||
| 197 | bb.warn('Stage times:\n %s' % '\n '.join(out)) | ||
