From patchwork Mon Oct 7 09:54:07 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 21526 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 554B2C32DD for ; Mon, 7 Oct 2024 09:54:45 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DAEAD63539; Mon, 7 Oct 2024 11:54:44 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="BIjfnJiF"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BE63E63532 for ; Mon, 7 Oct 2024 11:54:40 +0200 (CEST) Received: from ideasonboard.com (unknown [IPv6:2a00:6020:448c:6c00:ba12:1296:516b:1122]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A77911111; Mon, 7 Oct 2024 11:53:03 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1728294783; bh=n4SLJwD6XK6lTuOS7gyxMQ7vQzP/IusI2D3c6pPxqr0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BIjfnJiFJ1YdYVpq+6d6gmG/LH6sx2LFYBdamU3j3iIsyNMQzwdMjTFlrT0K8sgna bOX/8FojuL4S9aynQw0ZSRFS9oWGp1fWUhZHSIT5kxr6aUpsXl96ojy2hiS0ITGOPT QmGVAYLk8yddWcyTkammTgrQ4c+LfolAr+E+98O8= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH v2 3/7] utils: Add script to generate control_ids_debug.yaml Date: Mon, 7 Oct 2024 11:54:07 +0200 Message-ID: <20241007095425.211158-4-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20241007095425.211158-1-stefan.klug@ideasonboard.com> References: <20241007095425.211158-1-stefan.klug@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" For flexible debugging it is helpful to minimize the roundtrip time. This script parses the sourcetree and looks for usages of set(controls::debug::Something, and adds (or removes) the controls as necessary from the yaml description. It is meant to be used during development to ease the creation of the correct yaml entries. Signed-off-by: Stefan Klug --- Changes in v2: - Search only until the first comma of the set() call, to allow linebreaks there. - Support ruamel.yaml as fallback - Rename output to ctrl_file - Add "generated by" comment in yaml file --- utils/gen-debug-controls.py | 165 ++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100755 utils/gen-debug-controls.py diff --git a/utils/gen-debug-controls.py b/utils/gen-debug-controls.py new file mode 100755 index 000000000000..c5c4570ffd00 --- /dev/null +++ b/utils/gen-debug-controls.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2024, Ideas on Board Inc. +# +# Author: Stefan Klug +# +# This script looks for occurrences of the debug metadata controls in the source +# tree and updates src/libcamera/control_ids_debug.yaml accordingly. It is meant +# to be used during development to ease updating of the yaml file while +# debugging. + +import argparse +import logging +import os +import re +import sys +from dataclasses import dataclass +from pathlib import Path + +logger = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s') + +try: + import ruyaml +except: + try: + import ruamel.yaml as ruyaml + except: + logger.error( + f'Failed to import ruyaml. Please install the ruyaml or the ruamel.yaml package.') + sys.exit(1) + +@dataclass +class FoundMatch: + file: os.PathLike + whole_match: str + line: int + type: str + name: str + size: str = None + + +def get_control_name(control): + k = list(control.keys()) + if len(k) != 1: + raise Exception(f"Can't handle control entry with {len(k)} keys") + return k[0] + + +def find_debug_controls(dir): + extensions = ['.cpp', '.h'] + files = [p for p in dir.rglob('*') if p.suffix in extensions] + + # The following regex was tested on + # set>( controls::debug::something , static_cast(var) ) + # set<>( controls::debug::something , static_cast(var) ) + # set( controls::debug::something , static_cast (var) ) + exp = re.compile(r'set' # set function + # possibly followed by template param + r'(?:\<((?:[^)(])*)\>)?' + # referencing a debug control + r'\(\s*controls::debug::(\w+)\s*,' + ) + matches = [] + for p in files: + with p.open('r') as f: + for idx, line in enumerate(f): + match = exp.search(line) + if match: + m = FoundMatch(file=p, line=idx, type=match.group(1), + name=match.group(2), whole_match=match.group(0)) + if m.type is not None and m.type.startswith('Span'): + # simple span type detection treating the last word inside <> as type + r = re.match(r'Span<(?:.*\s+)(.*)>', m.type) + m.type = r.group(1) + m.size = '[n]' + matches.append(m) + return matches + + +def main(argv): + parser = argparse.ArgumentParser( + description='Automatically updates control_ids_debug.yaml',) + args = parser.parse_args(argv[1:]) + + yaml = ruyaml.YAML() + root_dir = Path(__file__).resolve().parent.parent + ctrl_file = root_dir.joinpath('src/libcamera/control_ids_debug.yaml') + + matches = find_debug_controls(root_dir.joinpath('src')) + + doc = yaml.load(ctrl_file) + + controls = doc['controls'] + + # create a map of names in the existing yaml for easier updating + controls_map = {} + for control in controls: + for k, v in control.items(): + controls_map[k] = v + + obsolete_names = list(controls_map.keys()) + + for m in matches: + if not m.type: + p = m.file.relative_to(Path.cwd(), walk_up=True) + logger.warning( + f'{p}:{m.line + 1}: Failed to deduce type from {m.whole_match} ... skipping') + continue + + p = m.file.relative_to(root_dir) + desc = {'type': m.type, + 'description': f'Debug control {m.name} found in {p}:{m.line}'} + if m.size is not None: + desc['size'] = m.size + + if m.name in controls_map: + # Can't use == for modified check because of the special yaml dicts. + update_needed = False + if list(controls_map[m.name].keys()) != list(desc.keys()): + update_needed = True + else: + for k, v in controls_map[m.name].items(): + if v != desc[k]: + update_needed = True + break + + if update_needed: + logger.info(f"Update control '{m.name}'") + controls_map[m.name].clear() + controls_map[m.name].update(desc) + + obsolete_names.remove(m.name) + else: + logger.info(f"Add control '{m.name}'") + insert_before = len(controls) + for idx, control in enumerate(controls): + if get_control_name(control).lower() > m.name.lower(): + insert_before = idx + break + controls.insert(insert_before, {m.name: desc}) + + # Remove elements from controls without recreating the list (to keep comments etc.) + idx = 0 + while idx < len(controls): + name = get_control_name(controls[idx]) + if name in obsolete_names: + logger.info(f"Remove control '{name}'") + controls.pop(idx) + else: + idx += 1 + + with ctrl_file.open('w') as f: + # ruyaml looses the copyright header + f.write(("# SPDX-License-Identifier: LGPL-2.1-or-later\n" + "#\n" + "# This file is generated by utils/gen-debug-controls.py\n" + "#\n")) + yaml.dump(doc, f) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv))