diff options
author | Elliot Smith <elliot.smith@intel.com> | 2016-03-08 11:32:12 +0000 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-03-09 22:45:16 +0000 |
commit | 0dcab0258e6e638db8b78fa3c7c7e485280712d4 (patch) | |
tree | 1d5fe7bafd6c45b8f935f0f516cc4d0309469eac /bitbake/lib/bb/ui/buildinfohelper.py | |
parent | cc74a8ae26a7728828a3442ba441b1676bc2c8a1 (diff) | |
download | poky-0dcab0258e6e638db8b78fa3c7c7e485280712d4.tar.gz |
bitbake: toaster: rework task buildstats storage and display
The data available from buildstats is now more fine grained than
previously, so take advantage of that to enrich the data we save
against tasks:
* Store the CPU usage for user and system separately, and display
them separately.
* Disk IO is now measured in bytes, not ms. Also store the
read/write bytes separately.
* Store started and ended times, as well as elapsed_time. This
will enable future features such as showing which tasks were
running at a particular point in the build.
There was also a problem with how we were looking up the Task
object, which meant that the buildstats were being added to
new tasks which weren't correctly associated with the build. Fix
how we look up the Task (only looking for tasks which match the
build, and the task and recipe names in the build stats data) so
the build stats are associated with the correct task.
[YOCTO #8842]
(Bitbake rev: efa6f915566b979bdbad233ae195b413cef1b8da)
Signed-off-by: Elliot Smith <elliot.smith@intel.com>
Signed-off-by: Michael Wood <michael.g.wood@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/bb/ui/buildinfohelper.py')
-rw-r--r-- | bitbake/lib/bb/ui/buildinfohelper.py | 72 |
1 files changed, 33 insertions, 39 deletions
diff --git a/bitbake/lib/bb/ui/buildinfohelper.py b/bitbake/lib/bb/ui/buildinfohelper.py index 81abede803..7fedb76911 100644 --- a/bitbake/lib/bb/ui/buildinfohelper.py +++ b/bitbake/lib/bb/ui/buildinfohelper.py | |||
@@ -50,6 +50,7 @@ from bb.msg import BBLogFormatter as formatter | |||
50 | from django.db import models | 50 | from django.db import models |
51 | from pprint import pformat | 51 | from pprint import pformat |
52 | import logging | 52 | import logging |
53 | from datetime import datetime, timedelta | ||
53 | 54 | ||
54 | from django.db import transaction, connection | 55 | from django.db import transaction, connection |
55 | 56 | ||
@@ -120,6 +121,12 @@ class ORMWrapper(object): | |||
120 | 121 | ||
121 | return vars(self)[dictname][key] | 122 | return vars(self)[dictname][key] |
122 | 123 | ||
124 | def _timestamp_to_datetime(self, secs): | ||
125 | """ | ||
126 | Convert timestamp in seconds to Python datetime | ||
127 | """ | ||
128 | return datetime(1970, 1, 1) + timedelta(seconds=secs) | ||
129 | |||
123 | # pylint: disable=no-self-use | 130 | # pylint: disable=no-self-use |
124 | # we disable detection of no self use in functions because the methods actually work on the object | 131 | # we disable detection of no self use in functions because the methods actually work on the object |
125 | # even if they don't touch self anywhere | 132 | # even if they don't touch self anywhere |
@@ -223,6 +230,28 @@ class ORMWrapper(object): | |||
223 | target.license_manifest_path = license_manifest_path | 230 | target.license_manifest_path = license_manifest_path |
224 | target.save() | 231 | target.save() |
225 | 232 | ||
233 | def update_task_object(self, build, task_name, recipe_name, task_stats): | ||
234 | """ | ||
235 | Find the task for build which matches the recipe and task name | ||
236 | to be stored | ||
237 | """ | ||
238 | task_to_update = Task.objects.get( | ||
239 | build = build, | ||
240 | task_name = task_name, | ||
241 | recipe__name = recipe_name | ||
242 | ) | ||
243 | |||
244 | task_to_update.started = self._timestamp_to_datetime(task_stats['started']) | ||
245 | task_to_update.ended = self._timestamp_to_datetime(task_stats['ended']) | ||
246 | task_to_update.elapsed_time = (task_stats['ended'] - task_stats['started']) | ||
247 | task_to_update.cpu_time_user = task_stats['cpu_time_user'] | ||
248 | task_to_update.cpu_time_system = task_stats['cpu_time_system'] | ||
249 | task_to_update.disk_io_read = task_stats['disk_io_read'] | ||
250 | task_to_update.disk_io_write = task_stats['disk_io_write'] | ||
251 | task_to_update.disk_io = task_stats['disk_io_read'] + task_stats['disk_io_write'] | ||
252 | |||
253 | task_to_update.save() | ||
254 | |||
226 | def get_update_task_object(self, task_information, must_exist = False): | 255 | def get_update_task_object(self, task_information, must_exist = False): |
227 | assert 'build' in task_information | 256 | assert 'build' in task_information |
228 | assert 'recipe' in task_information | 257 | assert 'recipe' in task_information |
@@ -259,14 +288,6 @@ class ORMWrapper(object): | |||
259 | task_object.sstate_result = Task.SSTATE_FAILED | 288 | task_object.sstate_result = Task.SSTATE_FAILED |
260 | object_changed = True | 289 | object_changed = True |
261 | 290 | ||
262 | # mark down duration if we have a start time and a current time | ||
263 | if 'start_time' in task_information.keys() and 'end_time' in task_information.keys(): | ||
264 | duration = task_information['end_time'] - task_information['start_time'] | ||
265 | task_object.elapsed_time = duration | ||
266 | object_changed = True | ||
267 | del task_information['start_time'] | ||
268 | del task_information['end_time'] | ||
269 | |||
270 | if object_changed: | 291 | if object_changed: |
271 | task_object.save() | 292 | task_object.save() |
272 | return task_object | 293 | return task_object |
@@ -1091,31 +1112,11 @@ class BuildInfoHelper(object): | |||
1091 | 1112 | ||
1092 | 1113 | ||
1093 | def store_tasks_stats(self, event): | 1114 | def store_tasks_stats(self, event): |
1094 | for (taskfile, taskname, taskstats, recipename) in BuildInfoHelper._get_data_from_event(event): | 1115 | task_data = BuildInfoHelper._get_data_from_event(event) |
1095 | localfilepath = taskfile.split(":")[-1] | ||
1096 | assert localfilepath.startswith("/") | ||
1097 | 1116 | ||
1098 | recipe_information = self._get_recipe_information_from_taskfile(taskfile) | 1117 | for (task_file, task_name, task_stats, recipe_name) in task_data: |
1099 | try: | 1118 | build = self.internal_state['build'] |
1100 | if recipe_information['file_path'].startswith(recipe_information['layer_version'].local_path): | 1119 | self.orm_wrapper.update_task_object(build, task_name, recipe_name, task_stats) |
1101 | recipe_information['file_path'] = recipe_information['file_path'][len(recipe_information['layer_version'].local_path):].lstrip("/") | ||
1102 | |||
1103 | recipe_object = Recipe.objects.get(layer_version = recipe_information['layer_version'], | ||
1104 | file_path__endswith = recipe_information['file_path'], | ||
1105 | name = recipename) | ||
1106 | except Recipe.DoesNotExist: | ||
1107 | logger.error("Could not find recipe for recipe_information %s name %s" , pformat(recipe_information), recipename) | ||
1108 | raise | ||
1109 | |||
1110 | task_information = {} | ||
1111 | task_information['build'] = self.internal_state['build'] | ||
1112 | task_information['recipe'] = recipe_object | ||
1113 | task_information['task_name'] = taskname | ||
1114 | task_information['cpu_usage'] = taskstats['cpu_usage'] | ||
1115 | task_information['disk_io'] = taskstats['disk_io'] | ||
1116 | if 'elapsed_time' in taskstats: | ||
1117 | task_information['elapsed_time'] = taskstats['elapsed_time'] | ||
1118 | self.orm_wrapper.get_update_task_object(task_information) | ||
1119 | 1120 | ||
1120 | def update_and_store_task(self, event): | 1121 | def update_and_store_task(self, event): |
1121 | assert 'taskfile' in vars(event) | 1122 | assert 'taskfile' in vars(event) |
@@ -1137,13 +1138,6 @@ class BuildInfoHelper(object): | |||
1137 | recipe = self.orm_wrapper.get_update_recipe_object(recipe_information, True) | 1138 | recipe = self.orm_wrapper.get_update_recipe_object(recipe_information, True) |
1138 | task_information = self._get_task_information(event,recipe) | 1139 | task_information = self._get_task_information(event,recipe) |
1139 | 1140 | ||
1140 | if 'time' in vars(event): | ||
1141 | if not 'start_time' in self.internal_state['taskdata'][identifier]: | ||
1142 | self.internal_state['taskdata'][identifier]['start_time'] = event.time | ||
1143 | else: | ||
1144 | task_information['end_time'] = event.time | ||
1145 | task_information['start_time'] = self.internal_state['taskdata'][identifier]['start_time'] | ||
1146 | |||
1147 | task_information['outcome'] = self.internal_state['taskdata'][identifier]['outcome'] | 1141 | task_information['outcome'] = self.internal_state['taskdata'][identifier]['outcome'] |
1148 | 1142 | ||
1149 | if 'logfile' in vars(event): | 1143 | if 'logfile' in vars(event): |