Received: by 2002:a6b:fb09:0:0:0:0:0 with SMTP id h9csp161611iog; Fri, 17 Jun 2022 00:44:34 -0700 (PDT) X-Google-Smtp-Source: AGRyM1uOb+orQCK/wOOfRsUs/GIVsqaMHKCrnDj9VIVyn4clwMAcL/jtnNrwBWeSDyVCqu+zy4ii X-Received: by 2002:a17:906:7217:b0:6fe:9448:6142 with SMTP id m23-20020a170906721700b006fe94486142mr7819103ejk.241.1655451873814; Fri, 17 Jun 2022 00:44:33 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1655451873; cv=none; d=google.com; s=arc-20160816; b=Zuv96In+onfjU+taFDXUtbtDFH0PQ8or/Yj9A5mUYZFSLhroIemD1l5zLPG+kHckqc R0GpUVFzSoaJjdxgzy/+uSdMCGHErSeyJzOqRx/YxJyhywfkgJRx979GFzBB2Dpsh//q 6zViC0vuJXj5ecSU8xscOiw6A+SURHIbVQY8IsKv7CObpKULzzmBZb+S7n+e8sHyBuyL g1fuhVMvhjY+93L1o1EAWnwp0mX2Zg5y6L9R4DX+FCMPnV1EqSHJbNzUnqwbZBmqMFSZ HbObm0ImRhqYHqL68GVGVgp+TOUyoMh/W4EEwesVHJonuqXr4Y04bazQfUfgjCKO0jtz cJ6Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:in-reply-to:from :references:cc:to:content-language:subject:user-agent:mime-version :date:message-id:dkim-signature; bh=JRsU1WrWNtkkJ5EjO/DomVSQN6uw4RapRxpCMJOwlrQ=; b=WY5uj/R/CUPqNl2aLpLUUzoMOqqf91t+bOHMGnTH6oxMW2WNWR/JED2rNSU6bixQ4M Ja2UZISysJ+Bma00EiIYnPQKoKmVKQIvbnm085xVcaIzOnSo7WGHIvKdcXJk/G+D4FAr al19pfhkgUCNlMXYRpq5HReaHcTOh6pxWn7mSJHZj++3aBTQXusR11PJ1ZmHkX4+mM1Z CJIC+olbnEsKMLLguFZWwM923zYiqu6ftwy6gbWL0BgLQCVUfLgguK3+ojTGbixdHb0X 99qlCx83l2Pf0ldXCzFsPP58w7fHrUFsov8iwh2HD6oaaLpDtn/wZIsQnLHv+1WnQ53d uy8A== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=dirPkFBJ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id h10-20020a05640250ca00b00435201d9705si5143507edb.602.2022.06.17.00.44.07; Fri, 17 Jun 2022 00:44:33 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=dirPkFBJ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1380317AbiFQGvc (ORCPT + 99 others); Fri, 17 Jun 2022 02:51:32 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60134 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232226AbiFQGvb (ORCPT ); Fri, 17 Jun 2022 02:51:31 -0400 Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 679D05C34C; Thu, 16 Jun 2022 23:51:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1655448689; x=1686984689; h=message-id:date:mime-version:subject:to:cc:references: from:in-reply-to:content-transfer-encoding; bh=v0FfUwdyis13vPTgRtAbqJHxaRqphsB1CizBc0dmw3w=; b=dirPkFBJhRBIrhft8Oh7dkBZbupe2oEGBDkoykVht29w3cBiEXJ1ljyz lkRMk+cnwevv14WdTO31ou8TqIzSQ0lO0Am86xI68xpXaiHUyhq34EJs7 OD8C0nEBuBfhquoP7OIrAuHaFDn5hRJlEGnJHI04qUhjop5NYzjRoXdum U/8+l2ltkMTxYgPmnxlmjXo7JztZUCeTJpjMpye+4Kkx8J9W+LII9GoHv /peDLAWhhjmZeAbKGP2rd0BnWjceBnOM2vawjDGT+JCT8hqc83ekNxQvD yZYqVFrQTMnkoNyz+R+fCyuJcxzJ4hwME/foa9evVtR2CP0OXXeGzUxiW A==; X-IronPort-AV: E=McAfee;i="6400,9594,10380"; a="280148312" X-IronPort-AV: E=Sophos;i="5.92,306,1650956400"; d="scan'208";a="280148312" Received: from fmsmga008.fm.intel.com ([10.253.24.58]) by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 16 Jun 2022 23:51:28 -0700 X-IronPort-AV: E=Sophos;i="5.92,306,1650956400"; d="scan'208";a="641916111" Received: from xingzhen-mobl.ccr.corp.intel.com (HELO [10.255.30.135]) ([10.255.30.135]) by fmsmga008-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 16 Jun 2022 23:51:22 -0700 Message-ID: <5e27d1e8-f329-7e11-2f5a-cc0d53ce1db3@linux.intel.com> Date: Fri, 17 Jun 2022 14:51:20 +0800 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.10.0 Subject: Re: [PATCH v5 2/4] perf jevents: Add python converter script Content-Language: en-US To: Ian Rogers Cc: Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Mark Rutland , Alexander Shishkin , Jiri Olsa , Namhyung Kim , John Garry , Kan Liang , Andi Kleen , Felix Fietkau , Qi Liu , Like Xu , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, Nick Forrington , Kajol Jain , James Clark , Andrew Kilroy , "Paul A . Clarke" , Will Deacon , Mathieu Poirier , ananth.narayan@amd.com, ravi.bangoria@amd.com, santosh.shukla@amd.com, sandipan.das@amd.com, Caleb Biggers , Perry Taylor , Kshipra Bopardikar , Stephane Eranian References: <20220616044806.47770-1-irogers@google.com> <20220616044806.47770-3-irogers@google.com> <7eb148e5-0763-1dbe-d1f5-80e5159be7c8@linux.intel.com> <3108fd92-c05b-961f-2367-81925535bf4f@linux.intel.com> From: Xing Zhengjun In-Reply-To: Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-7.4 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_EF,NICE_REPLY_A,RCVD_IN_DNSWL_MED, RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 6/17/2022 10:40 AM, Ian Rogers wrote: > On Thu, Jun 16, 2022 at 6:52 PM Xing Zhengjun > wrote: >> >> Hi Ian, >> >> On 6/17/2022 9:26 AM, Ian Rogers wrote: >>> On Thu, Jun 16, 2022 at 5:04 PM Xing Zhengjun >>> wrote: >>>> >>>> Hi Ian, >>>> >>>> I applied this patch series based on the head of the perf/core >>>> branch, but it failed when do make. >>>> My python version: >>>> Python 2.7.18 (default, Mar 8 2021, 13:02:45) >>>> [GCC 9.3.0] on linux2 >>>> >>>> >>>> GEN pmu-events/pmu-events.c >>>> make[3]: Nothing to be done for 'install_headers'. >>>> GEN python/perf.so >>>> Traceback (most recent call last): >>>> File "pmu-events/jevents.py", line 23, in >>>> def file_name_to_table_name(parents: list[str], dirname: str) -> str: >>>> TypeError: 'type' object is not subscriptable >>>> make[3]: *** [pmu-events/Build:20: pmu-events/pmu-events.c] Error 1 >>>> make[2]: *** [Makefile.perf:663: pmu-events/pmu-events-in.o] Error 2 >>>> make[2]: *** Waiting for unfinished jobs.... >>>> make[1]: *** [Makefile.perf:240: sub-make] Error 2 >>>> make: *** [Makefile:70: all] Error 2 >>> >>> Hi Zhengjun, >>> >>> Thanks for testing! You are running the test script and not the main >>> build. Unlike the main build I didn't add a Python version test for >>> the test script. As mentioned in the cover letter you need Python 3.6 >>> as Python 2 was sunset in 2020. >>> >> >> In fact, I run the main build, >> $ cd tools/perf >> $ make >> >> $ lsb_release -a >> No LSB modules are available. >> Distributor ID: Ubuntu >> Description: Ubuntu 20.04.3 LTS >> Release: 20.04 >> Codename: focal >> >> The default python in Ubuntu 20.04.3 is Python2 >> >> I think a lot of developers also use the Ubuntu 20.04.3 LTS, >> it's better to support python2. > > TL;DR "apt install python-dev-is-python3" :-) > > The problem is what you lose by making the code work with python2: > f-strings, various library functions. You end up coding to a worse > standard of Python and the code is considered non-pythonic. Python 3.6 > was released in Dec. 2016 and so is over 5 years old now - ie I'm not > pushing for the bleeding edge. > > A problem we face in the regular perf Python build is that Python 2 > doesn't support new APIs and Python3 has deprecated the old APIs [1]. > I think the sensible thing is to follow Python 3's deprecation plan > which will mean losing Python 2 support, ie we shouldn't look for > python2-config in the build any more as python2 won't support the APIs > our scripts are now using. > > I think arguing that Python 2 was something not worth sunsetting was > an argument to be made 2 years ago, but now we have to live with the > consequences. I don't think there is any hardship except an install > step. For the build changes in these patches if an old version of > Python is detected then you just don't get jevents and there is a > warning at build time. After "sudo apt install python-dev-is-python3", now the python version : Python 3.8.10 (default, Mar 15 2022, 12:22:08) [GCC 9.4.0] on linux But the issue still happened. $cd tools/perf $make GEN pmu-events/pmu-events.c make[3]: Nothing to be done for 'install_headers'. GEN python/perf.so Traceback (most recent call last): File "pmu-events/jevents.py", line 23, in def file_name_to_table_name(parents: list[str], dirname: str) -> str: TypeError: 'type' object is not subscriptable make[3]: *** [pmu-events/Build:20: pmu-events/pmu-events.c] Error 1 make[2]: *** [Makefile.perf:663: pmu-events/pmu-events-in.o] Error 2 make[2]: *** Waiting for unfinished jobs.... make[1]: *** [Makefile.perf:240: sub-make] Error 2 make: *** [Makefile:70: all] Error 2 For the python version, I think python3 is ok. But during the build, we need to add a warning and how to fix messages when detecting the system is using pthon2, just like it did before: Auto-detecting system features: ... dwarf: [ on ] ... dwarf_getlocations: [ on ] ... glibc: [ on ] ... libbfd: [ on ] ... libbfd-buildid: [ on ] ... libcap: [ on ] ... libelf: [ on ] ... libnuma: [ on ] ... numa_num_possible_cpus: [ on ] ... libperl: [ on ] ... libpython: [ on ] ... libcrypto: [ on ] ... libunwind: [ on ] ... libdw-dwarf-unwind: [ on ] ... zlib: [ on ] ... lzma: [ on ] ... get_cpuid: [ on ] ... bpf: [ on ] ... libaio: [ on ] ... libzstd: [ on ] ... disassembler-four-args: [ on ] ... python-dev-is-python3: [ off ] > > Thanks, > Ian > > [1] https://lore.kernel.org/lkml/20220615014206.26651-1-irogers@google.com/ > > >>> Thanks, >>> Ian >>> >>> >>>> On 6/16/2022 12:48 PM, Ian Rogers wrote: >>>>> jevents.c is large, has a dependency on an old forked version of jsmn, >>>>> and is challenging to work upon. A lot of jevents.c's complexity comes >>>>> from needing to write json and csv parsing from first principles. In >>>>> contrast python has this functionality in standard libraries and is >>>>> already a build pre-requisite for tools like asciidoc (that builds all >>>>> of the perf man pages). >>>>> >>>>> Introduce jevents.py that produces identical output to jevents.c. Add a >>>>> test that runs both converter tools and validates there are no output >>>>> differences. The test can be invoked with a phony build target like: >>>>> >>>>> make -C tools/perf jevents-py-test >>>>> >>>>> The python code deliberately tries to replicate the behavior of >>>>> jevents.c so that the output matches and transitioning tools shouldn't >>>>> introduce regressions. In some cases the code isn't as elegant as hoped, >>>>> but fixing this can be done as follow up. >>>>> >>>>> Signed-off-by: Ian Rogers >>>>> --- >>>>> tools/perf/Makefile.perf | 6 + >>>>> tools/perf/pmu-events/jevents-test.sh | 33 +++ >>>>> tools/perf/pmu-events/jevents.py | 394 ++++++++++++++++++++++++++ >>>>> 3 files changed, 433 insertions(+) >>>>> create mode 100755 tools/perf/pmu-events/jevents-test.sh >>>>> create mode 100755 tools/perf/pmu-events/jevents.py >>>>> >>>>> diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf >>>>> index 8f738e11356d..1e29c8936f71 100644 >>>>> --- a/tools/perf/Makefile.perf >>>>> +++ b/tools/perf/Makefile.perf >>>>> @@ -669,6 +669,12 @@ $(JEVENTS_IN): FORCE >>>>> $(JEVENTS): $(JEVENTS_IN) >>>>> $(QUIET_LINK)$(HOSTCC) $(JEVENTS_IN) -o $@ >>>>> >>>>> +JEVENTS_PY := pmu-events/jevents.py >>>>> +JEVENTS_PY_TEST := pmu-events/jevents-test.sh >>>>> +.PHONY: jevents-py-test >>>>> +jevents-py-test: $(JEVENTS) >>>>> + $(Q)$(call echo-cmd,gen)$(JEVENTS_PY_TEST) $(JEVENTS) $(JEVENTS_PY) pmu-events/arch >>>>> + >>>>> $(PMU_EVENTS_IN): $(JEVENTS) FORCE >>>>> $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events obj=pmu-events >>>>> >>>>> diff --git a/tools/perf/pmu-events/jevents-test.sh b/tools/perf/pmu-events/jevents-test.sh >>>>> new file mode 100755 >>>>> index 000000000000..9ae852292576 >>>>> --- /dev/null >>>>> +++ b/tools/perf/pmu-events/jevents-test.sh >>>>> @@ -0,0 +1,33 @@ >>>>> +#!/bin/sh >>>>> +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) >>>>> +# Validate that the legacy jevents and jevents.py produce identical output. >>>>> +set -e >>>>> + >>>>> +JEVENTS="$1" >>>>> +JEVENTS_PY="$2" >>>>> +ARCH_PATH="$3" >>>>> +JEVENTS_C_GENERATED=$(mktemp /tmp/jevents_c.XXXXX.c) >>>>> +JEVENTS_PY_GENERATED=$(mktemp /tmp/jevents_py.XXXXX.c) >>>>> + >>>>> +cleanup() { >>>>> + rm "$JEVENTS_C_GENERATED" "$JEVENTS_PY_GENERATED" >>>>> + trap - exit term int >>>>> +} >>>>> +trap cleanup exit term int >>>>> + >>>>> +for path in "$ARCH_PATH"/* >>>>> +do >>>>> + arch=$(basename $path) >>>>> + if [ "$arch" = "test" ] >>>>> + then >>>>> + continue >>>>> + fi >>>>> + echo "Checking architecture: $arch" >>>>> + echo "Generating using jevents.c" >>>>> + "$JEVENTS" "$arch" "$ARCH_PATH" "$JEVENTS_C_GENERATED" >>>>> + echo "Generating using jevents.py" >>>>> + "$JEVENTS_PY" "$arch" "$ARCH_PATH" "$JEVENTS_PY_GENERATED" >>>>> + echo "Diffing" >>>>> + diff -u "$JEVENTS_C_GENERATED" "$JEVENTS_PY_GENERATED" >>>>> +done >>>>> +cleanup >>>>> diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py >>>>> new file mode 100755 >>>>> index 000000000000..658eb2696f9a >>>>> --- /dev/null >>>>> +++ b/tools/perf/pmu-events/jevents.py >>>>> @@ -0,0 +1,394 @@ >>>>> +#!/usr/bin/env python3 >>>>> +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) >>>>> +"""Convert directories of JSON events to C code.""" >>>>> +import argparse >>>>> +import csv >>>>> +import json >>>>> +import os >>>>> +import sys >>>>> +from typing import Callable >>>>> + >>>>> +# Global command line arguments. >>>>> +args = None >>>>> +# List of event tables generated from "/sys" directories. >>>>> +sys_event_tables = [] >>>>> +# Map from an event name to an architecture standard >>>>> +# JsonEvent. Architecture standard events are in json files in the top >>>>> +# f'{args.starting_dir}/{args.arch}' directory. >>>>> +arch_std_events = dict() >>>>> +# Track whether an events table is currently being defined and needs closing. >>>>> +close_table = False >>>>> + >>>>> + >>>>> +def file_name_to_table_name(parents: list[str], dirname: str) -> str: >>>>> + """Generate a C table name from directory names.""" >>>>> + tblname = 'pme' >>>>> + for p in parents: >>>>> + tblname += '_' + p >>>>> + tblname += '_' + dirname >>>>> + return tblname.replace('-', '_') >>>>> + >>>>> + >>>>> +class JsonEvent: >>>>> + """Representation of an event loaded from a json file dictionary.""" >>>>> + >>>>> + def __init__(self, dict): >>>>> + """Constructor passed the dictionary of parsed json values.""" >>>>> + >>>>> + def llx(x: int) -> str: >>>>> + """Convert an int to a string similar to a printf modifier of %#llx.""" >>>>> + return '0' if x == 0 else hex(x) >>>>> + >>>>> + def fixdesc(s: str) -> str: >>>>> + """Fix formatting issue for the desc string.""" >>>>> + if s is None: >>>>> + return None >>>>> + return s.removesuffix('. ').removesuffix('. ').removesuffix('.').replace( >>>>> + '\n', '\\n').replace('\"', '\\"').replace('\r', '\\r') >>>>> + >>>>> + def convert_aggr_mode(aggr_mode: str) -> str: >>>>> + """Returns the aggr_mode_class enum value associated with the JSON string.""" >>>>> + if not aggr_mode: >>>>> + return None >>>>> + aggr_mode_to_enum = { >>>>> + 'PerChip': '1', >>>>> + 'PerCore': '2', >>>>> + } >>>>> + return aggr_mode_to_enum[aggr_mode] >>>>> + >>>>> + def lookup_msr(num: str) -> str: >>>>> + """Converts the msr number, or first in a list to the appropriate event field.""" >>>>> + if not num: >>>>> + return None >>>>> + msrmap = { >>>>> + 0x3F6: 'ldlat=', >>>>> + 0x1A6: 'offcore_rsp=', >>>>> + 0x1A7: 'offcore_rsp=', >>>>> + 0x3F7: 'frontend=', >>>>> + } >>>>> + return msrmap[int(num.split(',', 1)[0], 0)] >>>>> + >>>>> + def real_event(name: str, event: str) -> str: >>>>> + """Convert well known event names to an event string otherwise use the event argument.""" >>>>> + fixed = { >>>>> + 'inst_retired.any': 'event=0xc0,period=2000003', >>>>> + 'inst_retired.any_p': 'event=0xc0,period=2000003', >>>>> + 'cpu_clk_unhalted.ref': 'event=0x0,umask=0x03,period=2000003', >>>>> + 'cpu_clk_unhalted.thread': 'event=0x3c,period=2000003', >>>>> + 'cpu_clk_unhalted.core': 'event=0x3c,period=2000003', >>>>> + 'cpu_clk_unhalted.thread_any': 'event=0x3c,any=1,period=2000003', >>>>> + } >>>>> + if not name: >>>>> + return None >>>>> + if name.lower() in fixed: >>>>> + return fixed[name.lower()] >>>>> + return event >>>>> + >>>>> + def unit_to_pmu(unit: str) -> str: >>>>> + """Convert a JSON Unit to Linux PMU name.""" >>>>> + if not unit: >>>>> + return None >>>>> + # Comment brought over from jevents.c: >>>>> + # it's not realistic to keep adding these, we need something more scalable ... >>>>> + table = { >>>>> + 'CBO': 'uncore_cbox', >>>>> + 'QPI LL': 'uncore_qpi', >>>>> + 'SBO': 'uncore_sbox', >>>>> + 'iMPH-U': 'uncore_arb', >>>>> + 'CPU-M-CF': 'cpum_cf', >>>>> + 'CPU-M-SF': 'cpum_sf', >>>>> + 'UPI LL': 'uncore_upi', >>>>> + 'hisi_sicl,cpa': 'hisi_sicl,cpa', >>>>> + 'hisi_sccl,ddrc': 'hisi_sccl,ddrc', >>>>> + 'hisi_sccl,hha': 'hisi_sccl,hha', >>>>> + 'hisi_sccl,l3c': 'hisi_sccl,l3c', >>>>> + 'imx8_ddr': 'imx8_ddr', >>>>> + 'L3PMC': 'amd_l3', >>>>> + 'DFPMC': 'amd_df', >>>>> + 'cpu_core': 'cpu_core', >>>>> + 'cpu_atom': 'cpu_atom', >>>>> + } >>>>> + return table[unit] if unit in table else f'uncore_{unit.lower()}' >>>>> + >>>>> + eventcode = 0 >>>>> + if 'EventCode' in dict: >>>>> + eventcode = int(dict['EventCode'].split(',', 1)[0], 0) >>>>> + if 'ExtSel' in dict: >>>>> + eventcode |= int(dict['ExtSel']) << 8 >>>>> + configcode = int(dict['ConfigCode'], 0) if 'ConfigCode' in dict else None >>>>> + self.name = dict['EventName'].lower() if 'EventName' in dict else None >>>>> + self.compat = dict.get('Compat') >>>>> + self.desc = fixdesc(dict.get('BriefDescription')) >>>>> + self.long_desc = fixdesc(dict.get('PublicDescription')) >>>>> + precise = dict.get('PEBS') >>>>> + msr = lookup_msr(dict.get('MSRIndex')) >>>>> + msrval = dict.get('MSRValue') >>>>> + extra_desc = '' >>>>> + if 'Data_LA' in dict: >>>>> + extra_desc += ' Supports address when precise' >>>>> + if 'Errata' in dict: >>>>> + extra_desc += '.' >>>>> + if 'Errata' in dict: >>>>> + extra_desc += ' Spec update: ' + dict['Errata'] >>>>> + self.pmu = unit_to_pmu(dict.get('Unit')) >>>>> + filter = dict.get('Filter') >>>>> + self.unit = dict.get('ScaleUnit') >>>>> + self.perpkg = dict.get('PerPkg') >>>>> + self.aggr_mode = convert_aggr_mode(dict.get('AggregationMode')) >>>>> + self.deprecated = dict.get('Deprecated') >>>>> + self.metric_name = dict.get('MetricName') >>>>> + self.metric_group = dict.get('MetricGroup') >>>>> + self.metric_constraint = dict.get('MetricConstraint') >>>>> + self.metric_expr = dict.get('MetricExpr') >>>>> + if self.metric_expr: >>>>> + self.metric_expr = self.metric_expr.replace('\\', '\\\\') >>>>> + arch_std = dict.get('ArchStdEvent') >>>>> + if precise and self.desc and not '(Precise Event)' in self.desc: >>>>> + extra_desc += ' (Must be precise)' if precise == '2' else (' (Precise ' >>>>> + 'event)') >>>>> + event = f'config={llx(configcode)}' if configcode is not None else f'event={llx(eventcode)}' >>>>> + event_fields = [ >>>>> + ('AnyThread', 'any='), >>>>> + ('PortMask', 'ch_mask='), >>>>> + ('CounterMask', 'cmask='), >>>>> + ('EdgeDetect', 'edge='), >>>>> + ('FCMask', 'fc_mask='), >>>>> + ('Invert', 'inv='), >>>>> + ('SampleAfterValue', 'period='), >>>>> + ('UMask', 'umask='), >>>>> + ] >>>>> + for key, value in event_fields: >>>>> + if key in dict and dict[key] != '0': >>>>> + event += ',' + value + dict[key] >>>>> + if filter: >>>>> + event += f',{filter}' >>>>> + if msr: >>>>> + event += f',{msr}{msrval}' >>>>> + if self.desc and extra_desc: >>>>> + self.desc += extra_desc >>>>> + if self.long_desc and extra_desc: >>>>> + self.long_desc += extra_desc >>>>> + if self.pmu: >>>>> + if self.desc and not self.desc.endswith('. '): >>>>> + self.desc += '. ' >>>>> + self.desc = (self.desc if self.desc else '') + ('Unit: ' + self.pmu + ' ') >>>>> + if arch_std and arch_std.lower() in arch_std_events: >>>>> + event = arch_std_events[arch_std.lower()].event >>>>> + # Copy from the architecture standard event to self for undefined fields. >>>>> + for attr, value in arch_std_events[arch_std.lower()].__dict__.items(): >>>>> + if hasattr(self, attr) and not getattr(self, attr): >>>>> + setattr(self, attr, value) >>>>> + >>>>> + self.event = real_event(self.name, event) >>>>> + >>>>> + def __repr__(self) -> str: >>>>> + """String representation primarily for debugging.""" >>>>> + s = '{\n' >>>>> + for attr, value in self.__dict__.items(): >>>>> + if value: >>>>> + s += f'\t{attr} = {value},\n' >>>>> + return s + '}' >>>>> + >>>>> + def ToCString(self, topic_local: str) -> str: >>>>> + """Representation of the event as a C struct initializer.""" >>>>> + >>>>> + def AttrString(attr: str, value: str) -> str: >>>>> + return '\t.%s = \"%s\",\n' % (attr, value) >>>>> + >>>>> + def StrIfPresent(self, attr: str) -> str: >>>>> + if not getattr(self, attr): >>>>> + return '' >>>>> + return AttrString(attr, getattr(self, attr)) >>>>> + >>>>> + s = '{\n' >>>>> + for attr in ['name', 'event']: >>>>> + s += StrIfPresent(self, attr) >>>>> + if self.desc is not None: >>>>> + s += AttrString('desc', self.desc) >>>>> + else: >>>>> + s += AttrString('desc', '(null)') >>>>> + s += StrIfPresent(self, 'compat') >>>>> + s += f'\t.topic = "{topic_local}",\n' >>>>> + for attr in [ >>>>> + 'long_desc', 'pmu', 'unit', 'perpkg', 'aggr_mode', 'metric_expr', >>>>> + 'metric_name', 'metric_group', 'deprecated', 'metric_constraint' >>>>> + ]: >>>>> + s += StrIfPresent(self, attr) >>>>> + s += '},\n' >>>>> + return s >>>>> + >>>>> + >>>>> +def read_json_events(path: str) -> list[JsonEvent]: >>>>> + """Read json events from the specified file.""" >>>>> + return json.load(open(path), object_hook=lambda d: JsonEvent(d)) >>>>> + >>>>> +def preprocess_arch_std_files(archpath: str) -> None: >>>>> + """Read in all architecture standard events.""" >>>>> + global arch_std_events >>>>> + for item in os.scandir(archpath): >>>>> + if item.is_file() and item.name.endswith('.json'): >>>>> + for event in read_json_events(item.path): >>>>> + if event.name: >>>>> + arch_std_events[event.name.lower()] = event >>>>> + >>>>> + >>>>> +def print_events_table_prefix(tblname: str) -> None: >>>>> + """Called when a new events table is started.""" >>>>> + global close_table >>>>> + if close_table: >>>>> + raise IOError('Printing table prefix but last table has no suffix') >>>>> + args.output_file.write('static const struct pmu_event %s[] = {\n' % tblname) >>>>> + close_table = True >>>>> + >>>>> + >>>>> +def print_events_table_entries(item: os.DirEntry, topic: str) -> None: >>>>> + """Create contents of an events table.""" >>>>> + if not close_table: >>>>> + raise IOError('Table entries missing prefix') >>>>> + for event in read_json_events(item.path): >>>>> + args.output_file.write(event.ToCString(topic)) >>>>> + >>>>> +def print_events_table_suffix() -> None: >>>>> + """Optionally close events table.""" >>>>> + global close_table >>>>> + if close_table: >>>>> + args.output_file.write("""{ >>>>> +\t.name = 0, >>>>> +\t.event = 0, >>>>> +\t.desc = 0, >>>>> +}, >>>>> +}; >>>>> +""") >>>>> + close_table = False >>>>> + >>>>> +def process_one_file(parents: list[str], item: os.DirEntry) -> None: >>>>> + """Process a JSON file during the main walk.""" >>>>> + global sys_event_tables >>>>> + >>>>> + def get_topic(topic: str) -> str: >>>>> + return topic.removesuffix('.json').replace('-', ' ') >>>>> + >>>>> + def is_leaf_dir(path: str) -> bool: >>>>> + for item in os.scandir(path): >>>>> + if item.is_dir(): >>>>> + return False >>>>> + return True >>>>> + >>>>> + # model directory, reset topic >>>>> + if item.is_dir() and is_leaf_dir(item.path): >>>>> + print_events_table_suffix() >>>>> + >>>>> + tblname = file_name_to_table_name(parents, item.name) >>>>> + if item.name == 'sys': >>>>> + sys_event_tables.append(tblname) >>>>> + print_events_table_prefix(tblname) >>>>> + return >>>>> + >>>>> + # base dir or too deep >>>>> + level = len(parents) >>>>> + if level == 0 or level > 4: >>>>> + return >>>>> + >>>>> + # Ignore other directories. If the file name does not have a .json >>>>> + # extension, ignore it. It could be a readme.txt for instance. >>>>> + if not item.is_file() or not item.name.endswith('.json'): >>>>> + return >>>>> + >>>>> + print_events_table_entries(item, get_topic(item.name)) >>>>> + >>>>> + >>>>> +def print_mapping_table() -> None: >>>>> + """Read the mapfile and generate the struct from cpuid string to event table.""" >>>>> + table = csv.reader(open(f'{args.starting_dir}/{args.arch}/mapfile.csv')) >>>>> + args.output_file.write('const struct pmu_events_map pmu_events_map[] = {\n') >>>>> + first = True >>>>> + for row in table: >>>>> + # Skip the first row or any row beginning with #. >>>>> + if not first and len(row) > 0 and not row[0].startswith('#'): >>>>> + tblname = file_name_to_table_name([], row[2].replace('/', '_')) >>>>> + args.output_file.write("""{ >>>>> +\t.cpuid = \"%s\", >>>>> +\t.version = \"%s\", >>>>> +\t.type = \"%s\", >>>>> +\t.table = %s >>>>> +}, >>>>> +""" % (row[0].replace('\\', '\\\\'), row[1], row[3], tblname)) >>>>> + first = False >>>>> + >>>>> + args.output_file.write("""{ >>>>> +\t.cpuid = "testcpu", >>>>> +\t.version = "v1", >>>>> +\t.type = "core", >>>>> +\t.table = pme_test_soc_cpu, >>>>> +}, >>>>> +{ >>>>> +\t.cpuid = 0, >>>>> +\t.version = 0, >>>>> +\t.type = 0, >>>>> +\t.table = 0, >>>>> +}, >>>>> +}; >>>>> +""") >>>>> + >>>>> + >>>>> +def print_system_mapping_table() -> None: >>>>> + """C struct mapping table array for tables from /sys directories.""" >>>>> + args.output_file.write( >>>>> + '\nconst struct pmu_sys_events pmu_sys_event_tables[] = {\n') >>>>> + for tblname in sys_event_tables: >>>>> + args.output_file.write("""\t{ >>>>> +\t\t.table = %s, >>>>> +\t\t.name = \"%s\", >>>>> +\t}, >>>>> +""" % (tblname, tblname)) >>>>> + args.output_file.write("""\t{ >>>>> +\t\t.table = 0 >>>>> +\t}, >>>>> +}; >>>>> +""") >>>>> + >>>>> + >>>>> +def main() -> None: >>>>> + global args >>>>> + >>>>> + def dir_path(path: str) -> str: >>>>> + """Validate path is a directory for argparse.""" >>>>> + if os.path.isdir(path): >>>>> + return path >>>>> + else: >>>>> + raise argparse.ArgumentTypeError(f'\'{path}\' is not a valid directory') >>>>> + >>>>> + def ftw(path: str, parents: list[str], >>>>> + action: Callable[[list[str], os.DirEntry], None]) -> None: >>>>> + """Replicate the directory/file walking behavior of C's file tree walk.""" >>>>> + for item in os.scandir(path): >>>>> + action(parents, item) >>>>> + if item.is_dir(): >>>>> + ftw(item.path, parents + [item.name], action) >>>>> + >>>>> + ap = argparse.ArgumentParser() >>>>> + ap.add_argument('arch', help='Architecture name like x86') >>>>> + ap.add_argument( >>>>> + 'starting_dir', >>>>> + type=dir_path, >>>>> + help='Root of tree containing architecture directories containing json files' >>>>> + ) >>>>> + ap.add_argument( >>>>> + 'output_file', type=argparse.FileType('w'), nargs='?', default=sys.stdout) >>>>> + args = ap.parse_args() >>>>> + >>>>> + args.output_file.write("#include \"pmu-events/pmu-events.h\"\n") >>>>> + for path in [args.arch, 'test']: >>>>> + arch_path = f'{args.starting_dir}/{path}' >>>>> + if not os.path.isdir(arch_path): >>>>> + raise IOError(f'Missing architecture directory in \'{arch_path}\'') >>>>> + preprocess_arch_std_files(arch_path) >>>>> + ftw(arch_path, [], process_one_file) >>>>> + print_events_table_suffix() >>>>> + >>>>> + print_mapping_table() >>>>> + print_system_mapping_table() >>>>> + >>>>> + >>>>> +if __name__ == '__main__': >>>>> + main() >>>> >>>> -- >>>> Zhengjun Xing >> >> -- >> Zhengjun Xing -- Zhengjun Xing