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)) | ||