qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio
at jcs-hda-dma 273 lines 8.6 kB view raw
1#!/usr/bin/env python 2# QAPI texi generator 3# 4# This work is licensed under the terms of the GNU LGPL, version 2+. 5# See the COPYING file in the top-level directory. 6"""This script produces the documentation of a qapi schema in texinfo format""" 7 8from __future__ import print_function 9import re 10import qapi.common 11 12MSG_FMT = """ 13@deftypefn {type} {{}} {name} 14 15{body} 16@end deftypefn 17 18""".format 19 20TYPE_FMT = """ 21@deftp {{{type}}} {name} 22 23{body} 24@end deftp 25 26""".format 27 28EXAMPLE_FMT = """@example 29{code} 30@end example 31""".format 32 33 34def subst_strong(doc): 35 """Replaces *foo* by @strong{foo}""" 36 return re.sub(r'\*([^*\n]+)\*', r'@strong{\1}', doc) 37 38 39def subst_emph(doc): 40 """Replaces _foo_ by @emph{foo}""" 41 return re.sub(r'\b_([^_\n]+)_\b', r'@emph{\1}', doc) 42 43 44def subst_vars(doc): 45 """Replaces @var by @code{var}""" 46 return re.sub(r'@([\w-]+)', r'@code{\1}', doc) 47 48 49def subst_braces(doc): 50 """Replaces {} with @{ @}""" 51 return doc.replace('{', '@{').replace('}', '@}') 52 53 54def texi_example(doc): 55 """Format @example""" 56 # TODO: Neglects to escape @ characters. 57 # We should probably escape them in subst_braces(), and rename the 58 # function to subst_special() or subs_texi_special(). If we do that, we 59 # need to delay it until after subst_vars() in texi_format(). 60 doc = subst_braces(doc).strip('\n') 61 return EXAMPLE_FMT(code=doc) 62 63 64def texi_format(doc): 65 """ 66 Format documentation 67 68 Lines starting with: 69 - |: generates an @example 70 - =: generates @section 71 - ==: generates @subsection 72 - 1. or 1): generates an @enumerate @item 73 - */-: generates an @itemize list 74 """ 75 ret = '' 76 doc = subst_braces(doc) 77 doc = subst_vars(doc) 78 doc = subst_emph(doc) 79 doc = subst_strong(doc) 80 inlist = '' 81 lastempty = False 82 for line in doc.split('\n'): 83 empty = line == '' 84 85 # FIXME: Doing this in a single if / elif chain is 86 # problematic. For instance, a line without markup terminates 87 # a list if it follows a blank line (reaches the final elif), 88 # but a line with some *other* markup, such as a = title 89 # doesn't. 90 # 91 # Make sure to update section "Documentation markup" in 92 # docs/devel/qapi-code-gen.txt when fixing this. 93 if line.startswith('| '): 94 line = EXAMPLE_FMT(code=line[2:]) 95 elif line.startswith('= '): 96 line = '@section ' + line[2:] 97 elif line.startswith('== '): 98 line = '@subsection ' + line[3:] 99 elif re.match(r'^([0-9]*\.) ', line): 100 if not inlist: 101 ret += '@enumerate\n' 102 inlist = 'enumerate' 103 ret += '@item\n' 104 line = line[line.find(' ')+1:] 105 elif re.match(r'^[*-] ', line): 106 if not inlist: 107 ret += '@itemize %s\n' % {'*': '@bullet', 108 '-': '@minus'}[line[0]] 109 inlist = 'itemize' 110 ret += '@item\n' 111 line = line[2:] 112 elif lastempty and inlist: 113 ret += '@end %s\n\n' % inlist 114 inlist = '' 115 116 lastempty = empty 117 ret += line + '\n' 118 119 if inlist: 120 ret += '@end %s\n\n' % inlist 121 return ret 122 123 124def texi_body(doc): 125 """Format the main documentation body""" 126 return texi_format(doc.body.text) 127 128 129def texi_enum_value(value): 130 """Format a table of members item for an enumeration value""" 131 return '@item @code{%s}\n' % value.name 132 133 134def texi_member(member, suffix=''): 135 """Format a table of members item for an object type member""" 136 typ = member.type.doc_type() 137 membertype = ': ' + typ if typ else '' 138 return '@item @code{%s%s}%s%s\n' % ( 139 member.name, membertype, 140 ' (optional)' if member.optional else '', 141 suffix) 142 143 144def texi_members(doc, what, base, variants, member_func): 145 """Format the table of members""" 146 items = '' 147 for section in doc.args.values(): 148 # TODO Drop fallbacks when undocumented members are outlawed 149 if section.text: 150 desc = texi_format(section.text) 151 elif (variants and variants.tag_member == section.member 152 and not section.member.type.doc_type()): 153 values = section.member.type.member_names() 154 members_text = ', '.join(['@t{"%s"}' % v for v in values]) 155 desc = 'One of ' + members_text + '\n' 156 else: 157 desc = 'Not documented\n' 158 items += member_func(section.member) + desc 159 if base: 160 items += '@item The members of @code{%s}\n' % base.doc_type() 161 if variants: 162 for v in variants.variants: 163 when = ' when @code{%s} is @t{"%s"}' % ( 164 variants.tag_member.name, v.name) 165 if v.type.is_implicit(): 166 assert not v.type.base and not v.type.variants 167 for m in v.type.local_members: 168 items += member_func(m, when) 169 else: 170 items += '@item The members of @code{%s}%s\n' % ( 171 v.type.doc_type(), when) 172 if not items: 173 return '' 174 return '\n@b{%s:}\n@table @asis\n%s@end table\n' % (what, items) 175 176 177def texi_sections(doc): 178 """Format additional sections following arguments""" 179 body = '' 180 for section in doc.sections: 181 if section.name: 182 # prefer @b over @strong, so txt doesn't translate it to *Foo:* 183 body += '\n@b{%s:}\n' % section.name 184 if section.name and section.name.startswith('Example'): 185 body += texi_example(section.text) 186 else: 187 body += texi_format(section.text) 188 return body 189 190 191def texi_entity(doc, what, base=None, variants=None, 192 member_func=texi_member): 193 return (texi_body(doc) 194 + texi_members(doc, what, base, variants, member_func) 195 + texi_sections(doc)) 196 197 198class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): 199 def __init__(self, prefix): 200 self._prefix = prefix 201 self._gen = qapi.common.QAPIGenDoc() 202 self.cur_doc = None 203 204 def write(self, output_dir): 205 self._gen.write(output_dir, self._prefix + 'qapi-doc.texi') 206 207 def visit_enum_type(self, name, info, values, prefix): 208 doc = self.cur_doc 209 self._gen.add(TYPE_FMT(type='Enum', 210 name=doc.symbol, 211 body=texi_entity(doc, 'Values', 212 member_func=texi_enum_value))) 213 214 def visit_object_type(self, name, info, base, members, variants): 215 doc = self.cur_doc 216 if base and base.is_implicit(): 217 base = None 218 self._gen.add(TYPE_FMT(type='Object', 219 name=doc.symbol, 220 body=texi_entity(doc, 'Members', 221 base, variants))) 222 223 def visit_alternate_type(self, name, info, variants): 224 doc = self.cur_doc 225 self._gen.add(TYPE_FMT(type='Alternate', 226 name=doc.symbol, 227 body=texi_entity(doc, 'Members'))) 228 229 def visit_command(self, name, info, arg_type, ret_type, 230 gen, success_response, boxed, allow_oob): 231 doc = self.cur_doc 232 if boxed: 233 body = texi_body(doc) 234 body += ('\n@b{Arguments:} the members of @code{%s}\n' 235 % arg_type.name) 236 body += texi_sections(doc) 237 else: 238 body = texi_entity(doc, 'Arguments') 239 self._gen.add(MSG_FMT(type='Command', 240 name=doc.symbol, 241 body=body)) 242 243 def visit_event(self, name, info, arg_type, boxed): 244 doc = self.cur_doc 245 self._gen.add(MSG_FMT(type='Event', 246 name=doc.symbol, 247 body=texi_entity(doc, 'Arguments'))) 248 249 def symbol(self, doc, entity): 250 if self._gen._body: 251 self._gen.add('\n') 252 self.cur_doc = doc 253 entity.visit(self) 254 self.cur_doc = None 255 256 def freeform(self, doc): 257 assert not doc.args 258 if self._gen._body: 259 self._gen.add('\n') 260 self._gen.add(texi_body(doc) + texi_sections(doc)) 261 262 263def gen_doc(schema, output_dir, prefix): 264 if not qapi.common.doc_required: 265 return 266 vis = QAPISchemaGenDocVisitor(prefix) 267 vis.visit_begin(schema) 268 for doc in schema.docs: 269 if doc.symbol: 270 vis.symbol(doc, schema.lookup_entity(doc.symbol)) 271 else: 272 vis.freeform(doc) 273 vis.write(output_dir)