From patchwork Wed Oct 2 16:19:21 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 21474 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 4ADA6BD80A for ; Wed, 2 Oct 2024 16:20:22 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DED5C63522; Wed, 2 Oct 2024 18:20:21 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="p6JsRqk0"; 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 801DE63522 for ; Wed, 2 Oct 2024 18:20:20 +0200 (CEST) Received: from ideasonboard.com (unknown [IPv6:2a00:6020:448c:6c00:b246:b33f:c662:8ae1]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 87EF510D8; Wed, 2 Oct 2024 18:18:47 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1727885927; bh=h5CKhhbUcXkFYXZAwzQvEqeLYChhaFYFrU/ITQRA9TE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=p6JsRqk0vjLJLsDm0VLQf6CEqSV4mJDb3IhU+qik0KGnkNhHcPTWobGWiUtTnJ+I9 ROc/8zOcDwYTpKG6APQ46bC+eyXW6laHZbYqQT6Lwk1uxhOHUTc1xso/VZKavibQCI cXrv7zMOM/EyYmbNLXN1bcPk5fKa+yLEByq5YMd8= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH v1 3/8] utils: Add script to generate control_ids_debug.yaml Date: Wed, 2 Oct 2024 18:19:21 +0200 Message-ID: <20241002161933.247091-4-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20241002161933.247091-1-stefan.klug@ideasonboard.com> References: <20241002161933.247091-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 scipts parses the sourcetree and loos for usages of set(controls::debug::Something, ...) and adds (or removes) the controls as necessary from the yaml description. Signed-off-by: Stefan Klug --- utils/gen-debug-controls.py | 161 ++++++++++++++++++++++++++++++++++++ 1 file changed, 161 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..6cb087e1e0ae --- /dev/null +++ b/utils/gen-debug-controls.py @@ -0,0 +1,161 @@ +#!/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 + +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: + logger.error( + f'Failed to import ruyaml. Please install the ruyaml 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 + output = root_dir.joinpath('src/libcamera/control_ids_debug.yaml') + + matches = find_debug_controls(root_dir.joinpath('src')) + + doc = yaml.load(output) + + 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 output.open('w') as f: + # ruyaml looses the copyright header + f.write(("# SPDX-License-Identifier: LGPL-2.1-or-later\n" + "#\n" + "# Copyright (C) 2024, Ideas on Board Oy\n" + "#\n")) + yaml.dump(doc, f) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv))