All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf-next v2] scripts/bpf_doc.py: implement json output format
@ 2025-05-07 18:28 Ihor Solodrai
  2025-05-07 19:43 ` Quentin Monnet
  0 siblings, 1 reply; 3+ messages in thread
From: Ihor Solodrai @ 2025-05-07 18:28 UTC (permalink / raw)
  To: qmo, andrii
  Cc: bpf, ast, daniel, eddyz87, mykolal, dylan.reimerink, kernel-team

bpf_doc.py parses bpf.h header to collect information about various
API elements (such as BPF helpers) and then dump them in one of the
supported formats: rst docs and a C header.

It's useful for external tools to be able to consume this information
in an easy-to-parse format such as JSON. Implement JSON printers and
add --json command line argument.

v1: https://lore.kernel.org/bpf/20250506000605.497296-1-isolodrai@meta.com/

Signed-off-by: Ihor Solodrai <isolodrai@meta.com>
---
 scripts/bpf_doc.py | 112 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 98 insertions(+), 14 deletions(-)

diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py
index e74a01a85070..d669a0e16bf2 100755
--- a/scripts/bpf_doc.py
+++ b/scripts/bpf_doc.py
@@ -8,6 +8,7 @@
 from __future__ import print_function
 
 import argparse
+import json
 import re
 import sys, os
 import subprocess
@@ -43,6 +44,14 @@ class APIElement(object):
         self.ret = ret
         self.attrs = attrs
 
+    def to_dict(self):
+        return {
+            'proto': self.proto,
+            'desc': self.desc,
+            'ret': self.ret,
+            'attrs': self.attrs
+        }
+
 
 class Helper(APIElement):
     """
@@ -81,6 +90,11 @@ class Helper(APIElement):
 
         return res
 
+    def to_dict(self):
+        d = super().to_dict()
+        d.update(self.proto_break_down())
+        return d
+
 
 ATTRS = {
     '__bpf_fastcall': 'bpf_fastcall'
@@ -675,7 +689,7 @@ COMMANDS
         self.print_elem(command)
 
 
-class PrinterHelpers(Printer):
+class PrinterHelpersHeader(Printer):
     """
     A printer for dumping collected information about helpers as C header to
     be included from BPF program.
@@ -896,6 +910,43 @@ class PrinterHelpers(Printer):
         print(') = (void *) %d;' % helper.enum_val)
         print('')
 
+
+class PrinterHelpersJSON(Printer):
+    """
+    A printer for dumping collected information about helpers as a JSON file.
+    @parser: A HeaderParser with Helper objects
+    """
+
+    def __init__(self, parser):
+        self.elements = parser.helpers
+        self.elem_number_check(
+            parser.desc_unique_helpers,
+            parser.define_unique_helpers,
+            "helper",
+            "___BPF_FUNC_MAPPER",
+        )
+
+    def print_all(self):
+        helper_dicts = [helper.to_dict() for helper in self.elements]
+        out_dict = {'helpers': helper_dicts}
+        print(json.dumps(out_dict, indent=4))
+
+
+class PrinterSyscallJSON(Printer):
+    """
+    A printer for dumping collected syscall information as a JSON file.
+    @parser: A HeaderParser with APIElement objects
+    """
+
+    def __init__(self, parser):
+        self.elements = parser.commands
+        self.elem_number_check(parser.desc_syscalls, parser.enum_syscalls, 'syscall', 'bpf_cmd')
+
+    def print_all(self):
+        syscall_dicts = [syscall.to_dict() for syscall in self.elements]
+        out_dict = {'syscall': syscall_dicts}
+        print(json.dumps(out_dict, indent=4))
+
 ###############################################################################
 
 # If script is launched from scripts/ from kernel tree and can access
@@ -910,6 +961,19 @@ printers = {
         'syscall': PrinterSyscallRST,
 }
 
+# target -> output format -> printer
+printers = {
+    'helpers': {
+        'rst': PrinterHelpersRST,
+        'json': PrinterHelpersJSON,
+        'header': PrinterHelpersHeader,
+    },
+    'syscall': {
+        'rst': PrinterSyscallRST,
+        'json': PrinterSyscallJSON
+    },
+}
+
 argParser = argparse.ArgumentParser(description="""
 Parse eBPF header file and generate documentation for the eBPF API.
 The RST-formatted output produced can be turned into a manual page with the
@@ -917,6 +981,8 @@ rst2man utility.
 """)
 argParser.add_argument('--header', action='store_true',
                        help='generate C header file')
+argParser.add_argument('--json', action='store_true',
+                       help='generate a JSON')
 if (os.path.isfile(bpfh)):
     argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h',
                            default=bpfh)
@@ -924,17 +990,35 @@ else:
     argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h')
 argParser.add_argument('target', nargs='?', default='helpers',
                        choices=printers.keys(), help='eBPF API target')
-args = argParser.parse_args()
-
-# Parse file.
-headerParser = HeaderParser(args.filename)
-headerParser.run()
 
-# Print formatted output to standard output.
-if args.header:
-    if args.target != 'helpers':
-        raise NotImplementedError('Only helpers header generation is supported')
-    printer = PrinterHelpers(headerParser)
-else:
-    printer = printers[args.target](headerParser)
-printer.print_all()
+def error_die(message: str):
+    argParser.print_usage(file=sys.stderr)
+    print('Error: {}'.format(message), file=sys.stderr)
+    exit(1)
+
+def parse_and_dump():
+    args = argParser.parse_args()
+
+    # Parse file.
+    headerParser = HeaderParser(args.filename)
+    headerParser.run()
+
+    if args.header and args.json:
+        error_die('Use either --header or --json, not both')
+
+    output_format = 'rst'
+    if args.header:
+        output_format = 'header'
+    elif args.json:
+        output_format = 'json'
+
+    try:
+        printer = printers[args.target][output_format](headerParser)
+        # Print formatted output to standard output.
+        printer.print_all()
+    except KeyError:
+        error_die('Unsupported target/format combination: "{}", "{}"'
+                    .format(args.target, output_format))
+
+if __name__ == "__main__":
+    parse_and_dump()
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH bpf-next v2] scripts/bpf_doc.py: implement json output format
  2025-05-07 18:28 [PATCH bpf-next v2] scripts/bpf_doc.py: implement json output format Ihor Solodrai
@ 2025-05-07 19:43 ` Quentin Monnet
  2025-05-07 19:59   ` Ihor Solodrai
  0 siblings, 1 reply; 3+ messages in thread
From: Quentin Monnet @ 2025-05-07 19:43 UTC (permalink / raw)
  To: ihor.solodrai, andrii
  Cc: bpf, ast, daniel, eddyz87, mykolal, dylan.reimerink, kernel-team

2025-05-07 11:28 UTC-0700 ~ Ihor Solodrai <isolodrai@meta.com>
> bpf_doc.py parses bpf.h header to collect information about various
> API elements (such as BPF helpers) and then dump them in one of the
> supported formats: rst docs and a C header.
> 
> It's useful for external tools to be able to consume this information
> in an easy-to-parse format such as JSON. Implement JSON printers and
> add --json command line argument.
> 
> v1: https://lore.kernel.org/bpf/20250506000605.497296-1-isolodrai@meta.com/
> 
> Signed-off-by: Ihor Solodrai <isolodrai@meta.com>
> ---
>  scripts/bpf_doc.py | 112 +++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 98 insertions(+), 14 deletions(-)
> 
> diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py
> index e74a01a85070..d669a0e16bf2 100755
> --- a/scripts/bpf_doc.py
> +++ b/scripts/bpf_doc.py
> @@ -8,6 +8,7 @@
>  from __future__ import print_function
>  
>  import argparse
> +import json
>  import re
>  import sys, os
>  import subprocess
> @@ -43,6 +44,14 @@ class APIElement(object):
>          self.ret = ret
>          self.attrs = attrs
>  
> +    def to_dict(self):
> +        return {
> +            'proto': self.proto,
> +            'desc': self.desc,
> +            'ret': self.ret,
> +            'attrs': self.attrs
> +        }
> +
>  
>  class Helper(APIElement):
>      """
> @@ -81,6 +90,11 @@ class Helper(APIElement):
>  
>          return res
>  
> +    def to_dict(self):
> +        d = super().to_dict()
> +        d.update(self.proto_break_down())
> +        return d
> +
>  
>  ATTRS = {
>      '__bpf_fastcall': 'bpf_fastcall'
> @@ -675,7 +689,7 @@ COMMANDS
>          self.print_elem(command)
>  
>  
> -class PrinterHelpers(Printer):
> +class PrinterHelpersHeader(Printer):
>      """
>      A printer for dumping collected information about helpers as C header to
>      be included from BPF program.
> @@ -896,6 +910,43 @@ class PrinterHelpers(Printer):
>          print(') = (void *) %d;' % helper.enum_val)
>          print('')
>  
> +
> +class PrinterHelpersJSON(Printer):
> +    """
> +    A printer for dumping collected information about helpers as a JSON file.
> +    @parser: A HeaderParser with Helper objects
> +    """
> +
> +    def __init__(self, parser):
> +        self.elements = parser.helpers
> +        self.elem_number_check(
> +            parser.desc_unique_helpers,
> +            parser.define_unique_helpers,
> +            "helper",
> +            "___BPF_FUNC_MAPPER",
> +        )
> +
> +    def print_all(self):
> +        helper_dicts = [helper.to_dict() for helper in self.elements]
> +        out_dict = {'helpers': helper_dicts}
> +        print(json.dumps(out_dict, indent=4))
> +
> +
> +class PrinterSyscallJSON(Printer):
> +    """
> +    A printer for dumping collected syscall information as a JSON file.
> +    @parser: A HeaderParser with APIElement objects
> +    """
> +
> +    def __init__(self, parser):
> +        self.elements = parser.commands
> +        self.elem_number_check(parser.desc_syscalls, parser.enum_syscalls, 'syscall', 'bpf_cmd')
> +
> +    def print_all(self):
> +        syscall_dicts = [syscall.to_dict() for syscall in self.elements]
> +        out_dict = {'syscall': syscall_dicts}
> +        print(json.dumps(out_dict, indent=4))
> +
>  ###############################################################################
>  
>  # If script is launched from scripts/ from kernel tree and can access
> @@ -910,6 +961,19 @@ printers = {
>          'syscall': PrinterSyscallRST,
>  }


Can you please remove the old "printers" dict that is right above? It's
no longer used now that you redefine it below.

pw-bot: cr

>  
> +# target -> output format -> printer
> +printers = {
> +    'helpers': {
> +        'rst': PrinterHelpersRST,
> +        'json': PrinterHelpersJSON,
> +        'header': PrinterHelpersHeader,
> +    },
> +    'syscall': {
> +        'rst': PrinterSyscallRST,
> +        'json': PrinterSyscallJSON
> +    },
> +}
> +
>  argParser = argparse.ArgumentParser(description="""
>  Parse eBPF header file and generate documentation for the eBPF API.
>  The RST-formatted output produced can be turned into a manual page with the

[...]

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH bpf-next v2] scripts/bpf_doc.py: implement json output format
  2025-05-07 19:43 ` Quentin Monnet
@ 2025-05-07 19:59   ` Ihor Solodrai
  0 siblings, 0 replies; 3+ messages in thread
From: Ihor Solodrai @ 2025-05-07 19:59 UTC (permalink / raw)
  To: Quentin Monnet, andrii
  Cc: bpf, ast, daniel, eddyz87, mykolal, dylan.reimerink, kernel-team

On 2025-05-07 12:43 p.m., Quentin Monnet wrote:
> 2025-05-07 11:28 UTC-0700 ~ Ihor Solodrai <isolodrai@meta.com>
>> bpf_doc.py parses bpf.h header to collect information about various
>> API elements (such as BPF helpers) and then dump them in one of the
>> supported formats: rst docs and a C header.
>>
>> It's useful for external tools to be able to consume this information
>> in an easy-to-parse format such as JSON. Implement JSON printers and
>> add --json command line argument.
>>
>> v1: https://lore.kernel.org/bpf/20250506000605.497296-1-isolodrai@meta.com/
>>
>> Signed-off-by: Ihor Solodrai <isolodrai@meta.com>
>> ---
>>   scripts/bpf_doc.py | 112 +++++++++++++++++++++++++++++++++++++++------
>>   1 file changed, 98 insertions(+), 14 deletions(-)
>>
>> diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py
>> index e74a01a85070..d669a0e16bf2 100755
>> --- a/scripts/bpf_doc.py
>> +++ b/scripts/bpf_doc.py
>> @@ -8,6 +8,7 @@
>>   from __future__ import print_function
>>   
>>   import argparse
>> +import json
>>   import re
>>   import sys, os
>>   import subprocess
>> @@ -43,6 +44,14 @@ class APIElement(object):
>>           self.ret = ret
>>           self.attrs = attrs
>>   
>> +    def to_dict(self):
>> +        return {
>> +            'proto': self.proto,
>> +            'desc': self.desc,
>> +            'ret': self.ret,
>> +            'attrs': self.attrs
>> +        }
>> +
>>   
>>   class Helper(APIElement):
>>       """
>> @@ -81,6 +90,11 @@ class Helper(APIElement):
>>   
>>           return res
>>   
>> +    def to_dict(self):
>> +        d = super().to_dict()
>> +        d.update(self.proto_break_down())
>> +        return d
>> +
>>   
>>   ATTRS = {
>>       '__bpf_fastcall': 'bpf_fastcall'
>> @@ -675,7 +689,7 @@ COMMANDS
>>           self.print_elem(command)
>>   
>>   
>> -class PrinterHelpers(Printer):
>> +class PrinterHelpersHeader(Printer):
>>       """
>>       A printer for dumping collected information about helpers as C header to
>>       be included from BPF program.
>> @@ -896,6 +910,43 @@ class PrinterHelpers(Printer):
>>           print(') = (void *) %d;' % helper.enum_val)
>>           print('')
>>   
>> +
>> +class PrinterHelpersJSON(Printer):
>> +    """
>> +    A printer for dumping collected information about helpers as a JSON file.
>> +    @parser: A HeaderParser with Helper objects
>> +    """
>> +
>> +    def __init__(self, parser):
>> +        self.elements = parser.helpers
>> +        self.elem_number_check(
>> +            parser.desc_unique_helpers,
>> +            parser.define_unique_helpers,
>> +            "helper",
>> +            "___BPF_FUNC_MAPPER",
>> +        )
>> +
>> +    def print_all(self):
>> +        helper_dicts = [helper.to_dict() for helper in self.elements]
>> +        out_dict = {'helpers': helper_dicts}
>> +        print(json.dumps(out_dict, indent=4))
>> +
>> +
>> +class PrinterSyscallJSON(Printer):
>> +    """
>> +    A printer for dumping collected syscall information as a JSON file.
>> +    @parser: A HeaderParser with APIElement objects
>> +    """
>> +
>> +    def __init__(self, parser):
>> +        self.elements = parser.commands
>> +        self.elem_number_check(parser.desc_syscalls, parser.enum_syscalls, 'syscall', 'bpf_cmd')
>> +
>> +    def print_all(self):
>> +        syscall_dicts = [syscall.to_dict() for syscall in self.elements]
>> +        out_dict = {'syscall': syscall_dicts}
>> +        print(json.dumps(out_dict, indent=4))
>> +
>>   ###############################################################################
>>   
>>   # If script is launched from scripts/ from kernel tree and can access
>> @@ -910,6 +961,19 @@ printers = {
>>           'syscall': PrinterSyscallRST,
>>   }
> 
> 
> Can you please remove the old "printers" dict that is right above? It's
> no longer used now that you redefine it below.

Sure, missed it...
Thanks.

> 
> pw-bot: cr
> 
>>   
>> +# target -> output format -> printer
>> +printers = {
>> +    'helpers': {
>> +        'rst': PrinterHelpersRST,
>> +        'json': PrinterHelpersJSON,
>> +        'header': PrinterHelpersHeader,
>> +    },
>> +    'syscall': {
>> +        'rst': PrinterSyscallRST,
>> +        'json': PrinterSyscallJSON
>> +    },
>> +}
>> +
>>   argParser = argparse.ArgumentParser(description="""
>>   Parse eBPF header file and generate documentation for the eBPF API.
>>   The RST-formatted output produced can be turned into a manual page with the
> 
> [...]


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2025-05-07 19:59 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-07 18:28 [PATCH bpf-next v2] scripts/bpf_doc.py: implement json output format Ihor Solodrai
2025-05-07 19:43 ` Quentin Monnet
2025-05-07 19:59   ` Ihor Solodrai

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.