diff --git a/bitbake/lib/toaster/orm/models.py b/bitbake/lib/toaster/orm/models.py
index 588e1b9265..abc16d9b88 100644
--- a/bitbake/lib/toaster/orm/models.py
+++ b/bitbake/lib/toaster/orm/models.py
@@ -111,6 +111,15 @@ class Task(models.Model):
(OUTCOME_EMPTY, 'Empty'),
)
+ TASK_OUTCOME_HELP = {
+ OUTCOME_SUCCESS:'This task completed successfully',
+ OUTCOME_COVERED:'This task did not run because its output is provided by another task',
+ OUTCOME_CACHED:'This task restored output from the sstate-cache directory or mirrors',
+ OUTCOME_PREBUILT:'This task did not run because its outcome was reused from a previous build',
+ OUTCOME_FAILED:'This task did not complete',
+ OUTCOME_NA:''
+ }
+
search_allowed_fields = [ "recipe__name", "recipe__version", "task_name", "logfile" ]
objects = TaskManager()
@@ -118,6 +127,9 @@ class Task(models.Model):
def get_related_setscene(self):
return Task.objects.related_setscene(self)
+ def outcome_help(self):
+ return Task.TASK_OUTCOME_HELP[self.outcome]
+
def get_executed_display(self):
if self.task_executed:
return "Executed"
diff --git a/bitbake/lib/toaster/toastergui/templates/task.html b/bitbake/lib/toaster/toastergui/templates/task.html
new file mode 100644
index 0000000000..c5d6176b62
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/templates/task.html
@@ -0,0 +1,263 @@
+{% extends "basebuilddetailpage.html" %}
+
+{% load projecttags %}
+{% load humanize %}
+
+{% block localbreadcrumb %}
+
Tasks
+{{task.recipe.name}}_{{task.recipe.version}} {{task.task_name}}
+{% endblock %}
+
+{% block pagedetailinfomain %}
+
+
+
+
+{# Outcome section #}
+
+ {{task.get_outcome_display}}
+
+
+{%if task.task_executed %}
+ {# executed tasks outcome #}
+
+ -
+ Log file
+
+ -
+
{{task.logfile}}
+
+ {# show stack trace for failed task #}
+ {% if task.outcome == task.OUTCOME_FAILED and log_head %}
+ Python stack trace
+
+ {% endif %}
+
+{% else %}
+{# not executed tasks outcome #}
+ {% if task.outcome == task.OUTCOME_PREBUILT %}
+ {% if not showing_matches %}
+
Match to tasks in previous builds
+ {% elif matching_tasks %}
+
Prebuilt task could be based on
+
+
+
+ {% else %}
+
+ We have found no tasks matching this prebuilt task.
+ The task you are looking for could belong to a build for which Toaster has not data.
+
+ {% endif %}
+ {% elif task.outcome == task.OUTCOME_COVERED %}
+
+ -
+ Task covered by
+
+ -
+
+
+
+ {%elif task.outcome == task.OUTCOME_CACHED%}
+
+ -
+ Log file
+
+ -
+
{% for t in task.get_related_setscene %} {{t.logfile}}
{% endfor %}
+
+
+
+ {% endif %}
+{% endif %}
+
+{# Execution section #}
+
+ {% if task.task_executed %}
+ Executed
+
+ {% else %}
+ Not Executed
+
+ {% endif %}
+
+
+ -
+
+ Task inputs signature
+
+ -
+ {{task.sstate_checksum}}
+
+
+ {% if task.sstate_result != task.SSTATE_NA %}
+
Attempting to restore output from sstate cache
+
+
+
+ -
+
+ File searched for
+
+ {{task.path_to_sstate_obj}}
+ -
+
+ URI(s) searched
+
+ {{task.work_directory}}
+
+ {% endif %}
+ {% if task.sstate_result == task.SSTATE_MISS %}
+
+ File not in sstate cache. Running the real task instead.
+
+ {% elif task.sstate_result == task.SSTATE_FAILED%}
+
+ Failed to restore output from sstate cache. The file was found but could not be unpacked.
+
+
+ -
+
+ Log file
+
+ {{task.logfile}}
+ -
+
+ Time (secs)
+
+ - {{task.elapsed_time|format_none_and_zero}}
+
+
+ Running the real task instead.
+
+ {% elif task.sstate_result == task.SSTATE_RESTORED %}
+
+ Output successfully restored from sstate cache.
+
+ {% endif %}
+
+ -
+
+ Task order
+
+ - {{task.order}}
+ {% if task.task_executed %}
+ -
+
+ Task script type
+
+ - {{task.get_script_type_display}}
+ {% endif %}
+
+ -
+
+ Task depends on
+
+ -
+
+
+ -
+
+ Task reverse dependencies
+
+ -
+
+
+
+{# Performance section - shown only for executed tasks #}
+{%if task.task_executed %}
+
Performance
+
+ -
+
+ Time (secs)
+
+ - {{task.elapsed_time|format_none_and_zero}}
+ -
+
+ CPU usage
+
+ - {{task.cpu_usage|format_none_and_zero}}
+ -
+
+ Disk I/O (ms)
+
+ - {{task.disk_io|format_none_and_zero}}
+
+{%endif%}
+
+
+
+{% endblock %}
+
diff --git a/bitbake/lib/toaster/toastergui/templates/tasks.html b/bitbake/lib/toaster/toastergui/templates/tasks.html
index 7dc2c38b18..ce75b75c94 100644
--- a/bitbake/lib/toaster/toastergui/templates/tasks.html
+++ b/bitbake/lib/toaster/toastergui/templates/tasks.html
@@ -20,9 +20,9 @@
{% include "basetable_top.html" %}
{% for task in objects %}
-
+
|
- {{task.order}}
+ {{task.order}}
|
{{task.recipe.name}}
diff --git a/bitbake/lib/toaster/toastergui/templatetags/projecttags.py b/bitbake/lib/toaster/toastergui/templatetags/projecttags.py
index 7e2c8e98fa..b1e573b16d 100644
--- a/bitbake/lib/toaster/toastergui/templatetags/projecttags.py
+++ b/bitbake/lib/toaster/toastergui/templatetags/projecttags.py
@@ -70,13 +70,17 @@ def sortcols(tablecols):
return sorted(tablecols, key = lambda t: t['name'])
@register.filter
-def task_color(task_object):
- """ Return css class depending on Task execution status and execution outcome
+def task_color(task_object, show_green=False):
+ """ Return css class depending on Task execution status and execution outcome.
+ By default, green is not returned for executed and successful tasks;
+ show_green argument should be True to get green color.
"""
if not task_object.task_executed:
return 'class=muted'
elif task_object.outcome == task_object.OUTCOME_FAILED:
return 'class=error'
+ elif task_object.outcome == task_object.OUTCOME_SUCCESS and show_green:
+ return 'class=green'
else:
return ''
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index 11c8fd806e..b77be1a6e7 100644
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -349,12 +349,36 @@ def builddashboard(request, build_id):
return render(request, template, context)
def task(request, build_id, task_id):
- template = "singletask.html"
- if Build.objects.filter(pk=build_id).count() == 0 :
+ template = "task.html"
+ if Task.objects.filter(pk=task_id).count() == 0 :
return redirect(builds)
+ task = Task.objects.filter(pk=task_id)[0]
+
+ dependencies = sorted(_find_task_dep(task), key=lambda t:'%s_%s %s'%(t.recipe.name, t.recipe.version, t.task_name))
+ reverse_dependencies = sorted(_find_task_revdep(task), key=lambda t:'%s_%s %s'%(t.recipe.name, t.recipe.version, t.task_name))
+
+ log_head = ''
+ log_body = ''
+ if task.outcome == task.OUTCOME_FAILED:
+ pass
+# FIXME: the log should be read from the orm_logmessage table.
+# This will be fixed when the backend is done.
+
context = {
'build' : Build.objects.filter(pk=build_id)[0],
+ 'object': task,
+ 'task':task,
+ 'deps': dependencies,
+ 'rdeps': reverse_dependencies,
+ 'log_head':log_head,
+ 'log_body':log_body,
+ 'showing_matches':False,
}
+
+ if request.GET.get('show_matches', ""):
+ context['showing_matches'] = True
+ context['matching_tasks'] = Task.objects.filter(sstate_checksum=task.sstate_checksum).filter(build__completed_on__lt=task.build.completed_on).order_by('-build__completed_on')
+
return render(request, template, context)
def recipe(request, build_id, recipe_id):
@@ -388,6 +412,12 @@ def target(request, build_id, target_id):
return render(request, template, context)
+def _find_task_dep(task):
+ tp = []
+ for p in Task_Dependency.objects.filter(task=task):
+ tp.append(p.depends_on);
+ return tp
+
def _find_task_revdep(task):
tp = []
|