From mboxrd@z Thu Jan 1 00:00:00 1970 X-GM-THRID: 6942464545721942016 X-Received: by 2002:a05:651c:85:: with SMTP id 5mr9802390ljq.470.1616418488685; Mon, 22 Mar 2021 06:08:08 -0700 (PDT) X-BeenThere: isar-users@googlegroups.com Received: by 2002:ac2:58f8:: with SMTP id v24ls2599187lfo.2.gmail; Mon, 22 Mar 2021 06:08:07 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwGSgieM4Q8eOnR3o8I7oVMn5NUAhs+BboQHklJuhTwxCViwz8hfj6X5NvbRFoJ/IHBnFmR X-Received: by 2002:a19:7412:: with SMTP id v18mr8805515lfe.234.1616418487722; Mon, 22 Mar 2021 06:08:07 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1616418487; cv=none; d=google.com; s=arc-20160816; b=psNgiQ9EKUiO9eWfO4kL9fC/IMRo5rnVkfxrDwHcFnAD3CfvgscUeZV5fHoI0Fmr9e bXzQ2Dm5htw439NbkzhUjo2klkZzMahPrPr8Y+Nw1OYlIsj40qHKKXFueR3Jv3AXME0r 4XsVqhMZi8M3Uda+WxLJyC39f7KlfgI7luQ6gfnbF60hZDgLifOlimVU0k5ZN/r40a7T rEHyeP6NkyqRrKzCqkYAWoGqGcj8IzugOMloLhG7DzGgOAVPTEaPcH5P0bdWBOuhIOnL l1+ahvpft0l2FQul5qbVxqW2UdOnPOt9GRPGn3MEYEM4nVQGylvETzWbxGAe/NV0OgyA ITkA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from; bh=76bwx6/SBC4DsRlTvi/wqQfo7Xx7yZ0XBDh1sF2hlpg=; b=obHwKAEzYjNpB0n1Dqe0xSYUOgBOHtIWUoOBtvpCe7QL9PydATjT6s6XxT81bAvu3v MQU+cfi1ObDMecfhD+WK/B96EKmgDUBiTn4f70y5jQys+LuqbSXaicnHJqM7cdE6a6b0 v2GS1MssKRkOE6uA2IdusG6V58QyOFQ9cXq5DZobb98srDHaNjGkOyk/bFf94vxfwIGm +SAPoypp14iAO7t5Oz5xHvVOuJJ3w3HSg4lHKAHXlF/IDnh5ZeiCLM6VsRzcurAditYF LVRU+EK3UYVJswof7yDq74Vjs+VLG1VVAaKj8pJuUQL9A3jFBHCBkAkPXGXL605zZ7uF B7MQ== ARC-Authentication-Results: i=1; gmr-mx.google.com; spf=pass (google.com: domain of amikan@ilbers.de designates 85.214.156.166 as permitted sender) smtp.mailfrom=amikan@ilbers.de Return-Path: Received: from shymkent.ilbers.de (shymkent.ilbers.de. [85.214.156.166]) by gmr-mx.google.com with ESMTPS id h2si427799lja.3.2021.03.22.06.08.07 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Mon, 22 Mar 2021 06:08:07 -0700 (PDT) Received-SPF: pass (google.com: domain of amikan@ilbers.de designates 85.214.156.166 as permitted sender) client-ip=85.214.156.166; Authentication-Results: gmr-mx.google.com; spf=pass (google.com: domain of amikan@ilbers.de designates 85.214.156.166 as permitted sender) smtp.mailfrom=amikan@ilbers.de Received: from localhost.localdomain (mm-180-82-121-178.mgts.dynamic.pppoe.byfly.by [178.121.82.180] (may be forged)) (authenticated bits=0) by shymkent.ilbers.de (8.15.2/8.15.2/Debian-8) with ESMTPSA id 12MD85Fv020976 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 22 Mar 2021 14:08:06 +0100 From: Anton Mikanovich To: isar-users@googlegroups.com Cc: Anton Mikanovich Subject: [PATCH] charter: Introduce build chart generation tool Date: Mon, 22 Mar 2021 16:07:59 +0300 Message-Id: <20210322130759.152562-1-amikan@ilbers.de> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED autolearn=unavailable autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on shymkent.ilbers.de X-TUID: Qe6+HIYmLDpN This tool can generate Isar build charts from the logs prepend with timestamps in format: %Y-%m-%d %H:%M:%S. Signed-off-by: Anton Mikanovich --- testsuite/charter/charter.py | 145 +++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100755 testsuite/charter/charter.py diff --git a/testsuite/charter/charter.py b/testsuite/charter/charter.py new file mode 100755 index 0000000..36ced98 --- /dev/null +++ b/testsuite/charter/charter.py @@ -0,0 +1,145 @@ +#!/usr/bin/python3 + +import cairo +import re +import sys +from datetime import datetime + +timestamp_format = '%Y-%m-%d %H:%M:%S' + +try: + isar_log = sys.argv[1]; + output_filename = sys.argv[2]; +except: + print("Usage:", sys.argv[0], "isar.log output.svg") + print("Log time format:", timestamp_format) + print("Example: some_bitbake_command 2>&1 | gawk '{ print strftime(\"" + + timestamp_format + "\"), $0 }' > isar.log") + sys.exit(1) + +class Buildtask: + def __init__(self, package, task, start, stop): + self.package = package + self.task = task + self.start = start + self.stop = stop + +class Color: + def __init__(self, r, g, b): + self.r = r + self.g = g + self.b = b + +timestamp_first = 0 +timestamp_last = 0 +tasks = dict() + +log = open(isar_log) +for line in log: + line = line.rstrip() + if re.search('NOTE: recipe', line): + time = datetime.strptime(re.search('\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}', + line).group(0), timestamp_format).timestamp() + if timestamp_first == 0: + timestamp_first = time + timestamp_last = time + package = re.search('recipe\s(\S*):\stask\s\S*:\s\S*', line).group(1) + task = re.search('recipe\s\S*:\stask\s(\S*):\s\S*', line).group(1) + state = re.search('recipe\s\S*:\stask\s\S*:\s(\S*)', line).group(1) + key = package + ':' + task + if key in tasks and state == 'Succeeded': + tasks[key].stop = time - timestamp_first + elif state == 'Started': + tasks[key] = Buildtask(package, task, time - timestamp_first, -1) +log.close() + +header_height = 40 +footer_height = 5 +spacer_left = 5 +spacer_right = 250 +line_height = 18 +width_coef = 6 +image_width = spacer_left + width_coef * int(timestamp_last - + timestamp_first) + spacer_right +image_height = header_height + len(tasks) * line_height + footer_height + +def task_color(task): + return { + 'do_install_builddeps': Color(1.0, 0.9, 0.7), + 'do_dpkg_build': Color(0.9, 0.5, 0.4), + 'do_deploy_deb': Color(0.5, 0.9, 0.5), + 'do_rootfs_postprocess': Color(1.0, 0.4, 0.3) + }.get(task, Color(0.7, 0.8, 0.9)) + +def fill_background(r, g, b): + c.save() + c.set_source_rgb(r, g, b) + c.paint() + c.restore() + +def draw_text(x, y, text, size, color=None): + c.save() + c.move_to(x, y) + c.set_font_size(size) + if color: + c.set_source_rgb(color.r, color.g, color.b) + c.show_text(text) + c.restore() + +def draw_line(x, y, x2, y2, color, width=0.2): + c.save() + c.move_to(x, y) + c.set_source_rgb(color.r, color.g, color.b) + c.set_line_width(width) + c.line_to(x2, y2) + c.stroke() + c.restore() + +def draw_rect(x, y, width, height, color=None): + c.save() + c.rectangle(x, y, width, height) + if color: + c.set_source_rgb(color.r, color.g, color.b) + c.fill_preserve() + c.set_line_width(0.2) + c.set_source_rgb(0.8, 0.8, 0.8) + c.stroke() + c.restore() + +s = cairo.SVGSurface(output_filename, image_width, image_height) +c = cairo.Context(s) + +c.select_font_face("DejaVuSerif", cairo.FONT_SLANT_NORMAL, + cairo.FONT_WEIGHT_NORMAL) + +fill_background(1.0, 1.0, 1.0) +draw_text(1, 24, "Isar build chart from " + datetime.fromtimestamp( + timestamp_first).strftime(timestamp_format), 24, Color(0.1, 0.1, 0.1)) + +for n in range (int(timestamp_last - timestamp_first)): + if n % 5 == 0: + draw_text(spacer_left + n * width_coef, header_height - 1, + str(n) + "s", 10, Color(0.5, 0.5, 0.5)) + draw_line(spacer_left + n * width_coef, header_height, + spacer_left + n * width_coef, image_height - footer_height, + Color(0.8, 0.8, 0.8), 0.4) + else: + draw_line(spacer_left + n * width_coef, header_height, + spacer_left + n * width_coef, image_height - footer_height, + Color(0.9, 0.9, 0.9)) + +current_line = header_height +for key in tasks: + if tasks[key].stop < 0: + tasks[key].stop = int(timestamp_last - timestamp_first) + x1 = spacer_left + width_coef * tasks[key].start + width = width_coef * (tasks[key].stop - tasks[key].start) + if width <= 0: + width = 1 + draw_rect(x1, current_line, width, line_height, task_color(tasks[key].task)) + draw_text(x1 + 1, current_line + 12, '%s : %s (%ds)' % (tasks[key].package, + tasks[key].task, tasks[key].stop - tasks[key].start), 12, + Color(0.4, 0.4, 0.4)) + current_line += line_height + +s.finish() -- 2.20.1