diff options
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/checkpatch.pl | 562 | ||||
| -rw-r--r-- | scripts/cocci-macro-file.h | 119 | ||||
| -rw-r--r-- | scripts/dump-guest-memory.py | 2 | ||||
| -rw-r--r-- | scripts/qapi-commands.py | 345 | ||||
| -rw-r--r-- | scripts/qapi-event.py | 237 | ||||
| -rw-r--r-- | scripts/qapi-introspect.py | 213 | ||||
| -rw-r--r-- | scripts/qapi-types.py | 413 | ||||
| -rw-r--r-- | scripts/qapi-visit.py | 398 | ||||
| -rw-r--r-- | scripts/qapi.py | 791 | ||||
| -rw-r--r-- | scripts/qemu-gdb.py | 149 | ||||
| -rw-r--r-- | scripts/qemugdb/__init__.py | 28 | ||||
| -rw-r--r-- | scripts/qemugdb/coroutine.py | 91 | ||||
| -rw-r--r-- | scripts/qemugdb/mtree.py | 82 | ||||
| -rwxr-xr-x | scripts/qmp/qmp-shell | 35 | ||||
| -rwxr-xr-x | scripts/update-linux-headers.sh | 77 |
15 files changed, 1974 insertions, 1568 deletions
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 7f0aae977d..574334b985 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -141,44 +141,22 @@ our $Ident = qr{ }x; our $Storage = qr{extern|static|asmlinkage}; our $Sparse = qr{ - __user| - __kernel| - __force| - __iomem| - __must_check| - __init_refok| - __kprobes| - __ref + __force }x; # Notes to $Attribute: -# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check our $Attribute = qr{ const| - __percpu| - __nocast| - __safe| - __bitwise__| - __packed__| - __packed2__| - __naked| - __maybe_unused| - __always_unused| - __noreturn| - __used| - __cold| - __noclone| - __deprecated| - __read_mostly| - __kprobes| - __(?:mem|cpu|dev|)(?:initdata|initconst|init\b)| - ____cacheline_aligned| - ____cacheline_aligned_in_smp| - ____cacheline_internodealigned_in_smp| - __weak + volatile| + QEMU_NORETURN| + QEMU_WARN_UNUSED_RESULT| + QEMU_SENTINEL| + QEMU_ARTIFICIAL| + QEMU_PACKED| + GCC_FMT_ATTR }x; our $Modifier; -our $Inline = qr{inline|__always_inline|noinline}; +our $Inline = qr{inline}; our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; our $Lval = qr{$Ident(?:$Member)*}; @@ -215,14 +193,6 @@ our $typeTypedefs = qr{(?x: | QEMUBH # all uppercase )}; -our $logFunctions = qr{(?x: - printk| - pr_(debug|dbg|vdbg|devel|info|warning|err|notice|alert|crit|emerg|cont)| - (dev|netdev|netif)_(printk|dbg|vdbg|info|warn|err|notice|alert|crit|emerg|WARN)| - WARN| - panic -)}; - our @typeList = ( qr{void}, qr{(?:unsigned\s+)?char}, @@ -243,20 +213,20 @@ our @typeList = ( qr{${Ident}_handler}, qr{${Ident}_handler_fn}, ); + +# This can be modified by sub possible. Since it can be empty, be careful +# about regexes that always match, because they can cause infinite loops. our @modifierList = ( - qr{fastcall}, ); -our $allowed_asm_includes = qr{(?x: - irq| - memory -)}; -# memory.h: ARM has a custom one - sub build_types { - my $mods = "(?x: \n" . join("|\n ", @modifierList) . "\n)"; my $all = "(?x: \n" . join("|\n ", @typeList) . "\n)"; - $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; + if (@modifierList > 0) { + my $mods = "(?x: \n" . join("|\n ", @modifierList) . "\n)"; + $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; + } else { + $Modifier = qr{(?:$Attribute|$Sparse)}; + } $NonptrType = qr{ (?:$Modifier\s+|const\s+)* (?: @@ -277,27 +247,6 @@ build_types(); $chk_signoff = 0 if ($file); -my @dep_includes = (); -my @dep_functions = (); -my $removal = "Documentation/feature-removal-schedule.txt"; -if ($tree && -f "$root/$removal") { - open(my $REMOVE, '<', "$root/$removal") || - die "$P: $removal: open failed - $!\n"; - while (<$REMOVE>) { - if (/^Check:\s+(.*\S)/) { - for my $entry (split(/[, ]+/, $1)) { - if ($entry =~ m@include/(.*)@) { - push(@dep_includes, $1); - - } elsif ($entry !~ m@/@) { - push(@dep_functions, $entry); - } - } - } - } - close($REMOVE); -} - my @rawlines = (); my @lines = (); my $vname; @@ -633,7 +582,7 @@ sub statement_block_size { my ($stmt) = @_; $stmt =~ s/(^|\n)./$1/g; - $stmt =~ s/^\s*{//; + $stmt =~ s/^\s*\{//; $stmt =~ s/}\s*$//; $stmt =~ s/^\s*//; $stmt =~ s/\s*$//; @@ -1127,33 +1076,6 @@ sub CHK { } } -sub check_absolute_file { - my ($absolute, $herecurr) = @_; - my $file = $absolute; - - ##print "absolute<$absolute>\n"; - - # See if any suffix of this path is a path within the tree. - while ($file =~ s@^[^/]*/@@) { - if (-f "$root/$file") { - ##print "file<$file>\n"; - last; - } - } - if (! -f _) { - return 0; - } - - # It is, so see if the prefix is acceptable. - my $prefix = $absolute; - substr($prefix, -length($file)) = ''; - - ##print "prefix<$prefix>\n"; - if ($prefix ne ".../") { - WARN("use relative pathname instead of absolute in changelog text\n" . $herecurr); - } -} - sub process { my $filename = shift; @@ -1196,10 +1118,6 @@ sub process { my %suppress_export; # Pre-scan the patch sanitizing the lines. - # Pre-scan the patch looking for any __setup documentation. - # - my @setup_docs = (); - my $setup_docs = 0; sanitise_line_reset(); my $line; @@ -1207,13 +1125,6 @@ sub process { $linenr++; $line = $rawline; - if ($rawline=~/^\+\+\+\s+(\S+)/) { - $setup_docs = 0; - if ($1 =~ m@Documentation/kernel-parameters.txt$@) { - $setup_docs = 1; - } - #next; - } if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { $realline=$1-1; if (defined $2) { @@ -1272,10 +1183,6 @@ sub process { #print "==>$rawline\n"; #print "-->$line\n"; - - if ($setup_docs && $line =~ /^\+/) { - push(@setup_docs, $line); - } } $prefix = ''; @@ -1350,9 +1257,6 @@ sub process { WARN("patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); } - if ($realfile =~ m@^include/asm/@) { - ERROR("do not modify files in include/asm, change architecture specific files in include/asm-<architecture>\n" . "$here$rawline\n"); - } next; } @@ -1367,7 +1271,7 @@ sub process { # Check for incorrect file permissions if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { my $permhere = $here . "FILE: $realfile\n"; - if ($realfile =~ /(Makefile|Kconfig|\.c|\.cpp|\.h|\.S|\.tmpl)$/) { + if ($realfile =~ /(\bMakefile(?:\.objs)?|\.c|\.cc|\.cpp|\.h|\.mak|\.[sS])$/) { ERROR("do not set execute permissions for source files\n" . $permhere); } } @@ -1392,20 +1296,6 @@ sub process { $herecurr) if (!$emitted_corrupt++); } -# Check for absolute kernel paths. - if ($tree) { - while ($line =~ m{(?:^|\s)(/\S*)}g) { - my $file = $1; - - if ($file =~ m{^(.*?)(?::\d+)+:?$} && - check_absolute_file($1, $herecurr)) { - # - } else { - check_absolute_file($file, $herecurr); - } - } - } - # UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php if (($realfile =~ /^$/ || $line =~ /^\+/) && $rawline !~ m/^$UTF8*$/) { @@ -1432,45 +1322,12 @@ sub process { $rpt_cleaners = 1; } -# check for Kconfig help text having a real description -# Only applies when adding the entry originally, after that we do not have -# sufficient context to determine whether it is indeed long enough. - if ($realfile =~ /Kconfig/ && - $line =~ /\+\s*(?:---)?help(?:---)?$/) { - my $length = 0; - my $cnt = $realcnt; - my $ln = $linenr + 1; - my $f; - my $is_end = 0; - while ($cnt > 0 && defined $lines[$ln - 1]) { - $f = $lines[$ln - 1]; - $cnt-- if ($lines[$ln - 1] !~ /^-/); - $is_end = $lines[$ln - 1] =~ /^\+/; - $ln++; - - next if ($f =~ /^-/); - $f =~ s/^.//; - $f =~ s/#.*//; - $f =~ s/^\s+//; - next if ($f =~ /^$/); - if ($f =~ /^\s*config\s/) { - $is_end = 1; - last; - } - $length++; - } - WARN("please write a paragraph that describes the config symbol fully\n" . $herecurr) if ($is_end && $length < 4); - #print "is_end<$is_end> length<$length>\n"; - } - # check we are in a valid source file if not then ignore this hunk next if ($realfile !~ /\.(h|c|cpp|s|S|pl|sh)$/); #80 column limit - if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ && - $rawline !~ /^.\s*\*\s*\@$Ident\s/ && - !($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(KERN_\S+\s*|[^"]*))?"[X\t]*"\s*(?:,|\)\s*;)\s*$/ || - $line =~ /^\+\s*"[^"]*"\s*(?:\s*|,|\)\s*;)\s*$/) && + if ($line =~ /^\+/ && + !($line =~ /^\+\s*"[^"]*"\s*(?:\s*|,|\)\s*;)\s*$/) && $length > 80) { WARN("line over 80 characters\n" . $herecurr); @@ -1486,18 +1343,6 @@ sub process { WARN("adding a line without newline at end of file\n" . $herecurr); } -# Blackfin: use hi/lo macros - if ($realfile =~ m@arch/blackfin/.*\.S$@) { - if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("use the LO() macro, not (... & 0xFFFF)\n" . $herevet); - } - if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("use the HI() macro, not (... >> 16)\n" . $herevet); - } - } - # check we are in a valid source file C or perl if not then ignore this hunk next if ($realfile !~ /\.(h|c|cpp|pl)$/); @@ -1516,16 +1361,6 @@ sub process { WARN("CVS style keyword markers, these will _not_ be updated\n". $herecurr); } -# Blackfin: don't use __builtin_bfin_[cs]sync - if ($line =~ /__builtin_bfin_csync/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("use the CSYNC() macro in asm/blackfin.h\n" . $herevet); - } - if ($line =~ /__builtin_bfin_ssync/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("use the SSYNC() macro in asm/blackfin.h\n" . $herevet); - } - # Check for potential 'bare' types my ($stat, $cond, $line_nr_next, $remain_next, $off_next, $realline_next); @@ -1644,7 +1479,7 @@ sub process { # 79 or 80 characters, it is no longer possible to add a space and an # opening brace there) if ($#ctx == 0 && $ctx !~ /{\s*/ && - defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/ && + defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*\{/ && defined($lines[$ctx_ln - 2]) && length($lines[$ctx_ln - 2]) < 80) { ERROR("that open brace { should be on the previous line\n" . "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); @@ -1684,7 +1519,7 @@ sub process { my $continuation = 0; my $check = 0; $s =~ s/^.*\bdo\b//; - $s =~ s/^\s*{//; + $s =~ s/^\s*\{//; if ($s =~ s/^\s*\\//) { $continuation = 1; } @@ -1783,7 +1618,7 @@ sub process { } # check for initialisation to aggregates open brace on the next line - if ($line =~ /^.\s*{/ && + if ($line =~ /^.\s*\{/ && $prevline =~ /(?:^|[^=])=\s*$/) { ERROR("that open brace { should be on the previous line\n" . $hereprev); } @@ -1809,50 +1644,6 @@ sub process { $line =~ s@//.*@@; $opline =~ s@//.*@@; -# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider -# the whole statement. -#print "APW <$lines[$realline_next - 1]>\n"; - if (defined $realline_next && - exists $lines[$realline_next - 1] && - !defined $suppress_export{$realline_next} && - ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ || - $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { - # Handle definitions which produce identifiers with - # a prefix: - # XXX(foo); - # EXPORT_SYMBOL(something_foo); - my $name = $1; - if ($stat =~ /^.([A-Z_]+)\s*\(\s*($Ident)/ && - $name =~ /^${Ident}_$2/) { -#print "FOO C name<$name>\n"; - $suppress_export{$realline_next} = 1; - - } elsif ($stat !~ /(?: - \n.}\s*$| - ^.DEFINE_$Ident\(\Q$name\E\)| - ^.DECLARE_$Ident\(\Q$name\E\)| - ^.LIST_HEAD\(\Q$name\E\)| - ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(| - \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\() - )/x) { -#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n"; - $suppress_export{$realline_next} = 2; - } else { - $suppress_export{$realline_next} = 1; - } - } - if (!defined $suppress_export{$linenr} && - $prevline =~ /^.\s*$/ && - ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ || - $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { -#print "FOO B <$lines[$linenr - 1]>\n"; - $suppress_export{$linenr} = 2; - } - if (defined $suppress_export{$linenr} && - $suppress_export{$linenr} == 2) { - WARN("EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); - } - # check for global initialisers. if ($line =~ /^.$Type\s*$Ident\s*(?:\s+$Modifier)*\s*=\s*(0|NULL|false)\s*;/) { ERROR("do not initialise globals to 0 or NULL\n" . @@ -1900,56 +1691,27 @@ sub process { } } -# # no BUG() or BUG_ON() -# if ($line =~ /\b(BUG|BUG_ON)\b/) { -# print "Try to use WARN_ON & Recovery code rather than BUG() or BUG_ON()\n"; -# print "$herecurr"; -# $clean = 0; -# } - - if ($line =~ /\bLINUX_VERSION_CODE\b/) { - WARN("LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); - } - -# printk should use KERN_* levels. Note that follow on printk's on the -# same line do not need a level, so we use the current block context -# to try and find and validate the current printk. In summary the current -# printk includes all preceding printk's which have no newline on the end. -# we assume the first bad printk is the one to report. - if ($line =~ /\bprintk\((?!KERN_)\s*"/) { - my $ok = 0; - for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) { - #print "CHECK<$lines[$ln - 1]\n"; - # we have a preceding printk if it ends - # with "\n" ignore it, else it is to blame - if ($lines[$ln - 1] =~ m{\bprintk\(}) { - if ($rawlines[$ln - 1] !~ m{\\n"}) { - $ok = 1; - } - last; - } - } - if ($ok == 0) { - WARN("printk() should include KERN_ facility level\n" . $herecurr); - } - } - # function brace can't be on same line, except for #defines of do while, # or if closed on same line - if (($line=~/$Type\s*$Ident\(.*\).*\s{/) and - !($line=~/\#\s*define.*do\s{/) and !($line=~/}/)) { + if (($line=~/$Type\s*$Ident\(.*\).*\s\{/) and + !($line=~/\#\s*define.*do\s\{/) and !($line=~/}/)) { ERROR("open brace '{' following function declarations go on the next line\n" . $herecurr); } # open braces for enum, union and struct go on the same line. - if ($line =~ /^.\s*{/ && + if ($line =~ /^.\s*\{/ && $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { ERROR("open brace '{' following $1 go on the same line\n" . $hereprev); } +# ... however, open braces on typedef lines should be avoided. + if ($line =~ /^.\s*typedef\s+(enum|union|struct)(?:\s+$Ident\b)?.*[^;]$/) { + ERROR("typedefs should be separate from struct declaration\n" . $herecurr); + } + # missing space after union, struct or enum definition if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?(?:\s+$Ident)?[=\{]/) { - WARN("missing space after $1 definition\n" . $herecurr); + ERROR("missing space after $1 definition\n" . $herecurr); } # check for spacing round square brackets; allowed: @@ -2091,7 +1853,7 @@ sub process { # not required when having a single },{ on one line } elsif ($op eq ',') { if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/ && - ($elements[$n] . $elements[$n + 2]) !~ " *}{") { + ($elements[$n] . $elements[$n + 2]) !~ " *}\\{") { ERROR("space required after that '$op' $at\n" . $hereptr); } @@ -2190,29 +1952,9 @@ sub process { } } -# check for multiple assignments - if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { - CHK("multiple assignments should be avoided\n" . $herecurr); - } - -## # check for multiple declarations, allowing for a function declaration -## # continuation. -## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && -## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { -## -## # Remove any bracketed sections to ensure we do not -## # falsly report the parameters of functions. -## my $ln = $line; -## while ($ln =~ s/\([^\(\)]*\)//g) { -## } -## if ($ln =~ /,/) { -## WARN("declaring multiple variables together should be avoided\n" . $herecurr); -## } -## } - #need space before brace following if, while, etc - if (($line =~ /\(.*\){/ && $line !~ /\($Type\){/) || - $line =~ /do{/) { + if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) || + $line =~ /do\{/) { ERROR("space required before the open brace '{'\n" . $herecurr); } @@ -2267,7 +2009,7 @@ sub process { if ($line =~ /^.\s*return\s*(E[A-Z]*)\s*;/) { my $name = $1; if ($name ne 'EOF' && $name ne 'ERROR') { - CHK("return of an errno should typically be -ve (return -$1)\n" . $herecurr); + WARN("return of an errno should typically be -ve (return -$1)\n" . $herecurr); } } @@ -2398,22 +2140,6 @@ sub process { WARN("Whitepspace after \\ makes next lines useless\n" . $herecurr); } -#warn if <asm/foo.h> is #included and <linux/foo.h> is available (uses RAW line) - if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) { - my $file = "$1.h"; - my $checkfile = "include/linux/$file"; - if (-f "$root/$checkfile" && - $realfile ne $checkfile && - $1 !~ /$allowed_asm_includes/) - { - if ($realfile =~ m{^arch/}) { - CHK("Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr); - } else { - WARN("Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr); - } - } - } - # multi-statement macros should be enclosed in a do while loop, grab the # first statement and ensure its the whole macro if its not enclosed # in a known good container @@ -2508,15 +2234,6 @@ sub process { } } -# make sure symbols are always wrapped with VMLINUX_SYMBOL() ... -# all assignments may have only one of the following with an assignment: -# . -# ALIGN(...) -# VMLINUX_SYMBOL(...) - if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) { - WARN("vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr); - } - # check for missing bracing round if etc if ($line =~ /(^.*)\bif\b/ && $line !~ /\#\s*if/) { my ($level, $endln, @chunks) = @@ -2551,7 +2268,7 @@ sub process { my $spaced_block = $block; $spaced_block =~ s/\n\+/ /g; - $seen++ if ($spaced_block =~ /^\s*{/); + $seen++ if ($spaced_block =~ /^\s*\{/); print "APW: cond<$cond> block<$block> allowed<$allowed>\n" if $dbg_adv_apw; @@ -2644,64 +2361,23 @@ sub process { } } -# don't include deprecated include files (uses RAW line) - for my $inc (@dep_includes) { - if ($rawline =~ m@^.\s*\#\s*include\s*\<$inc>@) { - ERROR("Don't use <$inc>: see Documentation/feature-removal-schedule.txt\n" . $herecurr); - } - } - -# don't use deprecated functions - for my $func (@dep_functions) { - if ($line =~ /\b$func\b/) { - ERROR("Don't use $func(): see Documentation/feature-removal-schedule.txt\n" . $herecurr); - } - } - # no volatiles please my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { WARN("Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt\n" . $herecurr); } -# SPIN_LOCK_UNLOCKED & RW_LOCK_UNLOCKED are deprecated - if ($line =~ /\b(SPIN_LOCK_UNLOCKED|RW_LOCK_UNLOCKED)/) { - ERROR("Use of $1 is deprecated: see Documentation/spinlocks.txt\n" . $herecurr); - } - # warn about #if 0 if ($line =~ /^.\s*\#\s*if\s+0\b/) { - CHK("if this code is redundant consider removing it\n" . + WARN("if this code is redundant consider removing it\n" . $herecurr); } -# check for needless kfree() checks - if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { - my $expr = $1; - if ($line =~ /\bkfree\(\Q$expr\E\);/) { - WARN("kfree(NULL) is safe this check is probably not required\n" . $hereprev); - } - } -# check for needless usb_free_urb() checks +# check for needless g_free() checks if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { my $expr = $1; - if ($line =~ /\busb_free_urb\(\Q$expr\E\);/) { - WARN("usb_free_urb(NULL) is safe this check is probably not required\n" . $hereprev); - } - } - -# prefer usleep_range over udelay - if ($line =~ /\budelay\s*\(\s*(\w+)\s*\)/) { - # ignore udelay's < 10, however - if (! (($1 =~ /(\d+)/) && ($1 < 10)) ) { - CHK("usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $line); - } - } - -# warn about unexpectedly long msleep's - if ($line =~ /\bmsleep\s*\((\d+)\);/) { - if ($1 < 20) { - WARN("msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $line); + if ($line =~ /\bg_free\(\Q$expr\E\);/) { + WARN("g_free(NULL) is safe this check is probably not required\n" . $hereprev); } } @@ -2716,24 +2392,17 @@ sub process { if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { ERROR("exactly one space required after that #$1\n" . $herecurr); } - -# check for spinlock_t definitions without a comment. - if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ || - $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) { - my $which = $1; - if (!ctx_has_comment($first_line, $linenr)) { - CHK("$1 definition without comment\n" . $herecurr); - } - } # check for memory barriers without a comment. - if ($line =~ /\b(mb|rmb|wmb|read_barrier_depends|smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) { + if ($line =~ /\b(smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) { if (!ctx_has_comment($first_line, $linenr)) { - CHK("memory barrier without comment\n" . $herecurr); + WARN("memory barrier without comment\n" . $herecurr); } } # check of hardware specific defines - if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { - CHK("architecture specific defines should be avoided\n" . $herecurr); +# we have e.g. CONFIG_LINUX and CONFIG_WIN32 for common cases +# where they might be necessary. + if ($line =~ m@^.\s*\#\s*if.*\b__@) { + WARN("architecture specific defines should be avoided\n" . $herecurr); } # Check that the storage class is at the beginning of a declaration @@ -2748,11 +2417,6 @@ sub process { ERROR("inline keyword should sit between storage class and type\n" . $herecurr); } -# Check for __inline__ and __inline, prefer inline - if ($line =~ /\b(__inline__|__inline)\b/) { - WARN("plain inline is preferred over $1\n" . $herecurr); - } - # check for sizeof(&) if ($line =~ /\bsizeof\s*\(\s*\&/) { WARN("sizeof(& should be avoided\n" . $herecurr); @@ -2785,98 +2449,55 @@ sub process { WARN("externs should be avoided in .c files\n" . $herecurr); } -# checks for new __setup's - if ($rawline =~ /\b__setup\("([^"]*)"/) { - my $name = $1; - - if (!grep(/$name/, @setup_docs)) { - CHK("__setup appears un-documented -- check Documentation/kernel-parameters.txt\n" . $herecurr); +# check for pointless casting of g_malloc return + if ($line =~ /\*\s*\)\s*g_(try)?(m|re)alloc(0?)(_n)?\b/) { + if ($2 == 'm') { + WARN("unnecessary cast may hide bugs, use g_$1new$3 instead\n" . $herecurr); + } else { + WARN("unnecessary cast may hide bugs, use g_$1renew$3 instead\n" . $herecurr); } } -# check for pointless casting of kmalloc return - if ($line =~ /\*\s*\)\s*k[czm]alloc\b/) { - WARN("unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); - } - # check for gcc specific __FUNCTION__ if ($line =~ /__FUNCTION__/) { WARN("__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr); } -# check for semaphores used as mutexes - if ($line =~ /^.\s*(DECLARE_MUTEX|init_MUTEX)\s*\(/) { - WARN("mutexes are preferred for single holder semaphores\n" . $herecurr); - } -# check for semaphores used as mutexes - if ($line =~ /^.\s*init_MUTEX_LOCKED\s*\(/) { - WARN("consider using a completion\n" . $herecurr); - - } -# recommend strict_strto* over simple_strto* - if ($line =~ /\bsimple_(strto.*?)\s*\(/) { - WARN("consider using strict_$1 in preference to simple_$1\n" . $herecurr); +# recommend qemu_strto* over strto* + if ($line =~ /\b(strto.*?)\s*\(/) { + WARN("consider using qemu_$1 in preference to $1\n" . $herecurr); } -# check for __initcall(), use device_initcall() explicitly please - if ($line =~ /^.\s*__initcall\s*\(/) { - WARN("please use device_initcall() instead of __initcall()\n" . $herecurr); +# check for module_init(), use category-specific init macros explicitly please + if ($line =~ /^module_init\s*\(/) { + WARN("please use block_init(), type_init() etc. instead of module_init()\n" . $herecurr); } # check for various ops structs, ensure they are const. - my $struct_ops = qr{acpi_dock_ops| - address_space_operations| - backlight_ops| - block_device_operations| - dentry_operations| - dev_pm_ops| - dma_map_ops| - extent_io_ops| - file_lock_operations| - file_operations| - hv_ops| - ide_dma_ops| - intel_dvo_dev_ops| - item_operations| - iwl_ops| - kgdb_arch| - kgdb_io| - kset_uevent_ops| - lock_manager_operations| - microcode_ops| - mtrr_ops| - neigh_ops| - nlmsvc_binding| - pci_raw_ops| - pipe_buf_operations| - platform_hibernation_ops| - platform_suspend_ops| - proto_ops| - rpc_pipe_ops| - seq_operations| - snd_ac97_build_ops| - soc_pcmcia_socket_ops| - stacktrace_ops| - sysfs_ops| - tty_operations| - usb_mon_operations| - wd_ops}x; + my $struct_ops = qr{AIOCBInfo| + BdrvActionOps| + BlockDevOps| + BlockJobDriver| + DisplayChangeListenerOps| + GraphicHwOps| + IDEDMAOps| + KVMCapabilityInfo| + MemoryRegionIOMMUOps| + MemoryRegionOps| + MemoryRegionPortio| + QEMUFileOps| + SCSIBusInfo| + SCSIReqOps| + Spice[A-Z][a-zA-Z0-9]*Interface| + TPMDriverOps| + USBDesc[A-Z][a-zA-Z0-9]*| + VhostOps| + VMStateDescription| + VMStateInfo}x; if ($line !~ /\bconst\b/ && - $line =~ /\bstruct\s+($struct_ops)\b/) { + $line =~ /\b($struct_ops)\b/) { WARN("struct $1 should normally be const\n" . $herecurr); } -# use of NR_CPUS is usually wrong -# ignore definitions of NR_CPUS and usage to define arrays as likely right - if ($line =~ /\bNR_CPUS\b/ && - $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ && - $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ && - $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ && - $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ && - $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/) - { - WARN("usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); - } - # check for %L{u,d,i} in strings my $string; while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { @@ -2888,25 +2509,6 @@ sub process { } } -# whine mightly about in_atomic - if ($line =~ /\bin_atomic\s*\(/) { - if ($realfile =~ m@^drivers/@) { - ERROR("do not use in_atomic in drivers\n" . $herecurr); - } elsif ($realfile !~ m@^kernel/@) { - WARN("use of in_atomic() is incorrect outside core kernel code\n" . $herecurr); - } - } - -# check for lockdep_set_novalidate_class - if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || - $line =~ /__lockdep_no_validate__\s*\)/ ) { - if ($realfile !~ m@^kernel/lockdep@ && - $realfile !~ m@^include/linux/lockdep@ && - $realfile !~ m@^drivers/base/core@) { - ERROR("lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr); - } - } - # QEMU specific tests if ($rawline =~ /\b(?:Qemu|QEmu)\b/) { WARN("use QEMU instead of Qemu or QEmu\n" . $herecurr); diff --git a/scripts/cocci-macro-file.h b/scripts/cocci-macro-file.h new file mode 100644 index 0000000000..eceb4be73f --- /dev/null +++ b/scripts/cocci-macro-file.h @@ -0,0 +1,119 @@ +/* Macro file for Coccinelle + * + * Copyright (C) 2015 Red Hat, Inc. + * + * Authors: + * Paolo Bonzini <pbonzini@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or, at your + * option, any later version. See the COPYING file in the top-level directory. + */ + +/* Coccinelle only does limited parsing of headers, and chokes on some idioms + * defined in compiler.h and queue.h. Macros that Coccinelle must know about + * in order to parse .c files must be in a separate macro file---which is + * exactly what you're staring at now. + * + * To use this file, add the "--macro-file scripts/cocci-macro-file.h" to the + * Coccinelle command line. + */ + +/* From qemu/compiler.h */ +#define QEMU_GNUC_PREREQ(maj, min) 1 +#define QEMU_NORETURN __attribute__ ((__noreturn__)) +#define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#define QEMU_SENTINEL __attribute__((sentinel)) +#define QEMU_ARTIFICIAL __attribute__((always_inline, artificial)) +#define QEMU_PACKED __attribute__((gcc_struct, packed)) + +#define cat(x,y) x ## y +#define cat2(x,y) cat(x,y) +#define QEMU_BUILD_BUG_ON(x) \ + typedef char cat2(qemu_build_bug_on__,__LINE__)[(x)?-1:1] __attribute__((unused)); + +#define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m))) + +#define xglue(x, y) x ## y +#define glue(x, y) xglue(x, y) +#define stringify(s) tostring(s) +#define tostring(s) #s + +#define typeof_field(type, field) typeof(((type *)0)->field) +#define type_check(t1,t2) ((t1*)0 - (t2*)0) + +/* From qemu/queue.h */ + +#define QLIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define QLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define QLIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * Singly-linked List definitions. + */ +#define QSLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define QSLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define QSLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Simple queue definitions. + */ +#define QSIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define QSIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define QSIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Tail queue definitions. + */ +#define Q_TAILQ_HEAD(name, type, qual) \ +struct name { \ + qual type *tqh_first; /* first element */ \ + qual type *qual *tqh_last; /* addr of last next element */ \ +} +#define QTAILQ_HEAD(name, type) \ +struct name { \ + type *tqh_first; /* first element */ \ + type **tqh_last; /* addr of last next element */ \ +} + +#define QTAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define Q_TAILQ_ENTRY(type, qual) \ +struct { \ + qual type *tqe_next; /* next element */ \ + qual type *qual *tqe_prev; /* address of previous next element */\ +} +#define QTAILQ_ENTRY(type) \ +struct { \ + type *tqe_next; /* next element */ \ + type **tqe_prev; /* address of previous next element */ \ +} diff --git a/scripts/dump-guest-memory.py b/scripts/dump-guest-memory.py index dc8e44acf8..08796fff8c 100644 --- a/scripts/dump-guest-memory.py +++ b/scripts/dump-guest-memory.py @@ -118,7 +118,7 @@ shape and this command should mostly work.""" def qemu_get_ram_block(self, ram_addr): ram_blocks = gdb.parse_and_eval("ram_list.blocks") for block in self.qlist_foreach(ram_blocks, "next"): - if (ram_addr - block["offset"] < block["length"]): + if (ram_addr - block["offset"] < block["used_length"]): return block raise gdb.GdbError("Bad ram offset %x" % ram_addr) diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index ca22acc1d5..810a897625 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -12,143 +12,147 @@ # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. -from ordereddict import OrderedDict from qapi import * import re -def generate_command_decl(name, args, ret_type): - arglist="" - for argname, argtype, optional in parse_args(args): - argtype = c_type(argtype, is_param=True) - if optional: - arglist += "bool has_%s, " % c_name(argname) - arglist += "%s %s, " % (argtype, c_name(argname)) + +def gen_command_decl(name, arg_type, ret_type): return mcgen(''' -%(ret_type)s qmp_%(name)s(%(args)sError **errp); +%(c_type)s qmp_%(c_name)s(%(params)s); ''', - ret_type=c_type(ret_type), name=c_name(name), - args=arglist).strip() + c_type=(ret_type and ret_type.c_type()) or 'void', + c_name=c_name(name), + params=gen_params(arg_type, 'Error **errp')) + -def gen_err_check(errvar): - if errvar: - return mcgen(''' -if (local_err) { +def gen_err_check(err): + if not err: + return '' + return mcgen(''' +if (%(err)s) { goto out; } -''') - return '' +''', + err=err) + -def gen_sync_call(name, args, ret_type, indent=0): - ret = "" - arglist="" - retval="" +def gen_call(name, arg_type, ret_type): + ret = '' + + argstr = '' + if arg_type: + for memb in arg_type.members: + if memb.optional: + argstr += 'has_%s, ' % c_name(memb.name) + argstr += '%s, ' % c_name(memb.name) + + lhs = '' if ret_type: - retval = "retval = " - for argname, argtype, optional in parse_args(args): - if optional: - arglist += "has_%s, " % c_name(argname) - arglist += "%s, " % (c_name(argname)) - push_indent(indent) + lhs = 'retval = ' + + push_indent() ret = mcgen(''' -%(retval)sqmp_%(name)s(%(args)s&local_err); +%(lhs)sqmp_%(c_name)s(%(args)s&local_err); ''', - name=c_name(name), args=arglist, retval=retval).rstrip() + c_name=c_name(name), args=argstr, lhs=lhs) if ret_type: - ret += "\n" + gen_err_check('local_err') - ret += "\n" + mcgen('''' -%(marshal_output_call)s -''', - marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip() - pop_indent(indent) - return ret.rstrip() + ret += gen_err_check('local_err') + ret += mcgen(''' +qmp_marshal_output_%(c_name)s(retval, ret, &local_err); +''', + c_name=ret_type.c_name()) + pop_indent() + return ret -def gen_marshal_output_call(name, ret_type): - if not ret_type: - return "" - return "qmp_marshal_output_%s(retval, ret, &local_err);" % c_name(name) -def gen_visitor_input_containers_decl(args, obj): - ret = "" +def gen_marshal_vars(arg_type, ret_type): + ret = mcgen(''' + Error *local_err = NULL; +''') push_indent() - if len(args) > 0: + + if ret_type: ret += mcgen(''' -QmpInputVisitor *mi = qmp_input_visitor_new_strict(%(obj)s); -QapiDeallocVisitor *md; -Visitor *v; +%(c_type)s retval; ''', - obj=obj) - pop_indent() + c_type=ret_type.c_type()) - return ret.rstrip() + if arg_type: + ret += mcgen(''' +QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args)); +QapiDeallocVisitor *md; +Visitor *v; +''') -def gen_visitor_input_vars_decl(args): - ret = "" - push_indent() - for argname, argtype, optional in parse_args(args): - if optional: - ret += mcgen(''' -bool has_%(argname)s = false; -''', - argname=c_name(argname)) - if is_c_ptr(argtype): - ret += mcgen(''' -%(argtype)s %(argname)s = NULL; + for memb in arg_type.members: + if memb.optional: + ret += mcgen(''' +bool has_%(c_name)s = false; ''', - argname=c_name(argname), argtype=c_type(argtype)) - else: + c_name=c_name(memb.name)) ret += mcgen(''' -%(argtype)s %(argname)s = {0}; +%(c_type)s %(c_name)s = %(c_null)s; ''', - argname=c_name(argname), argtype=c_type(argtype)) + c_name=c_name(memb.name), + c_type=memb.type.c_type(), + c_null=memb.type.c_null()) + ret += '\n' + else: + ret += mcgen(''' + +(void)args; +''') pop_indent() - return ret.rstrip() + return ret -def gen_visitor_input_block(args, dealloc=False): - ret = "" - errparg = '&local_err' - errarg = 'local_err' - if len(args) == 0: +def gen_marshal_input_visit(arg_type, dealloc=False): + ret = '' + + if not arg_type: return ret push_indent() if dealloc: errparg = 'NULL' - errarg = None; + errarg = None ret += mcgen(''' qmp_input_visitor_cleanup(mi); md = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(md); ''') else: + errparg = '&local_err' + errarg = 'local_err' ret += mcgen(''' v = qmp_input_get_visitor(mi); ''') - for argname, argtype, optional in parse_args(args): - if optional: + for memb in arg_type.members: + if memb.optional: ret += mcgen(''' visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s); ''', - c_name=c_name(argname), name=argname, errp=errparg) + c_name=c_name(memb.name), name=memb.name, + errp=errparg) ret += gen_err_check(errarg) ret += mcgen(''' if (has_%(c_name)s) { ''', - c_name=c_name(argname)) + c_name=c_name(memb.name)) push_indent() ret += mcgen(''' -visit_type_%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s); +visit_type_%(c_type)s(v, &%(c_name)s, "%(name)s", %(errp)s); ''', - c_name=c_name(argname), name=argname, argtype=argtype, - visitor=type_name(argtype), errp=errparg) + c_name=c_name(memb.name), name=memb.name, + c_type=memb.type.c_name(), errp=errparg) ret += gen_err_check(errarg) - if optional: + if memb.optional: pop_indent() ret += mcgen(''' } @@ -159,14 +163,13 @@ visit_type_%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s); qapi_dealloc_visitor_cleanup(md); ''') pop_indent() - return ret.rstrip() + return ret -def gen_marshal_output(name, args, ret_type, middle_mode): - if not ret_type: - return "" - ret = mcgen(''' -static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp) +def gen_marshal_output(ret_type): + return mcgen(''' + +static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp) { Error *local_err = NULL; QmpOutputVisitor *mo = qmp_output_visitor_new(); @@ -174,7 +177,7 @@ static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_o Visitor *v; v = qmp_output_get_visitor(mo); - visit_type_%(visitor)s(v, &ret_in, "unused", &local_err); + visit_type_%(c_name)s(v, &ret_in, "unused", &local_err); if (local_err) { goto out; } @@ -185,101 +188,118 @@ out: qmp_output_visitor_cleanup(mo); md = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(md); - visit_type_%(visitor)s(v, &ret_in, "unused", NULL); + visit_type_%(c_name)s(v, &ret_in, "unused", NULL); qapi_dealloc_visitor_cleanup(md); } ''', - c_ret_type=c_type(ret_type), c_name=c_name(name), - visitor=type_name(ret_type)) + c_type=ret_type.c_type(), c_name=ret_type.c_name()) - return ret -def gen_marshal_input_decl(name, args, ret_type, middle_mode): - ret = 'void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name) +def gen_marshal_proto(name): + ret = 'void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name) if not middle_mode: - ret = "static " + ret + ret = 'static ' + ret return ret -def gen_marshal_input(name, args, ret_type, middle_mode): - hdr = gen_marshal_input_decl(name, args, ret_type, middle_mode) - - ret = mcgen(''' -%(header)s -{ - Error *local_err = NULL; -''', - header=hdr) - if ret_type: - if is_c_ptr(ret_type): - retval = " %s retval = NULL;" % c_type(ret_type) - else: - retval = " %s retval;" % c_type(ret_type) - ret += mcgen(''' -%(retval)s +def gen_marshal_decl(name): + return mcgen(''' +%(proto)s; ''', - retval=retval) + proto=gen_marshal_proto(name)) - if len(args) > 0: - ret += mcgen(''' -%(visitor_input_containers_decl)s -%(visitor_input_vars_decl)s -%(visitor_input_block)s +def gen_marshal(name, arg_type, ret_type): + ret = mcgen(''' +%(proto)s +{ ''', - visitor_input_containers_decl=gen_visitor_input_containers_decl(args, "QOBJECT(args)"), - visitor_input_vars_decl=gen_visitor_input_vars_decl(args), - visitor_input_block=gen_visitor_input_block(args)) - else: - ret += mcgen(''' + proto=gen_marshal_proto(name)) - (void)args; -''') + ret += gen_marshal_vars(arg_type, ret_type) + ret += gen_marshal_input_visit(arg_type) + ret += gen_call(name, arg_type, ret_type) - ret += mcgen(''' -%(sync_call)s -''', - sync_call=gen_sync_call(name, args, ret_type, indent=4)) - if re.search('^ *goto out\\;', ret, re.MULTILINE): + if re.search('^ *goto out;', ret, re.MULTILINE): ret += mcgen(''' out: ''') ret += mcgen(''' error_propagate(errp, local_err); -%(visitor_input_block_cleanup)s +''') + ret += gen_marshal_input_visit(arg_type, dealloc=True) + ret += mcgen(''' } -''', - visitor_input_block_cleanup=gen_visitor_input_block(args, - dealloc=True)) +''') return ret -def gen_registry(commands): - registry="" + +def gen_register_command(name, success_response): push_indent() - for cmd in commands: - options = 'QCO_NO_OPTIONS' - if not cmd.get('success-response', True): - options = 'QCO_NO_SUCCESS_RESP' + options = 'QCO_NO_OPTIONS' + if not success_response: + options = 'QCO_NO_SUCCESS_RESP' - registry += mcgen(''' -qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s); + ret = mcgen(''' +qmp_register_command("%(name)s", qmp_marshal_%(c_name)s, %(opts)s); ''', - name=cmd['command'], c_name=c_name(cmd['command']), - opts=options) + name=name, c_name=c_name(name), + opts=options) pop_indent() + return ret + + +def gen_registry(registry): ret = mcgen(''' + static void qmp_init_marshal(void) { -%(registry)s +''') + ret += registry + ret += mcgen(''' } qapi_init(qmp_init_marshal); -''', - registry=registry.rstrip()) +''') return ret + +class QAPISchemaGenCommandVisitor(QAPISchemaVisitor): + def __init__(self): + self.decl = None + self.defn = None + self._regy = None + self._visited_ret_types = None + + def visit_begin(self, schema): + self.decl = '' + self.defn = '' + self._regy = '' + self._visited_ret_types = set() + + def visit_end(self): + if not middle_mode: + self.defn += gen_registry(self._regy) + self._regy = None + self._visited_ret_types = None + + def visit_command(self, name, info, arg_type, ret_type, + gen, success_response): + if not gen: + return + self.decl += gen_command_decl(name, arg_type, ret_type) + if ret_type and ret_type not in self._visited_ret_types: + self._visited_ret_types.add(ret_type) + self.defn += gen_marshal_output(ret_type) + if middle_mode: + self.decl += gen_marshal_decl(name) + self.defn += gen_marshal(name, arg_type, ret_type) + if not middle_mode: + self._regy += gen_register_command(name, success_response) + + middle_mode = False (input_file, output_dir, do_c, do_h, prefix, opts) = \ @@ -289,10 +309,6 @@ for o, a in opts: if o in ("-m", "--middle"): middle_mode = True -exprs = parse_schema(input_file) -commands = filter(lambda expr: expr.has_key('command'), exprs) -commands = filter(lambda expr: not expr.has_key('gen'), commands) - c_comment = ''' /* * schema-defined QMP->QAPI command dispatch @@ -340,7 +356,7 @@ fdef.write(mcgen(''' #include "%(prefix)sqmp-commands.h" ''', - prefix=prefix)) + prefix=prefix)) fdecl.write(mcgen(''' #include "%(prefix)sqapi-types.h" @@ -348,29 +364,12 @@ fdecl.write(mcgen(''' #include "qapi/error.h" ''', - prefix=prefix)) - -for cmd in commands: - arglist = [] - ret_type = None - if cmd.has_key('data'): - arglist = cmd['data'] - if cmd.has_key('returns'): - ret_type = cmd['returns'] - ret = generate_command_decl(cmd['command'], arglist, ret_type) + "\n" - fdecl.write(ret) - if ret_type: - ret = gen_marshal_output(cmd['command'], arglist, ret_type, middle_mode) + "\n" - fdef.write(ret) - - if middle_mode: - fdecl.write('%s;\n' % gen_marshal_input_decl(cmd['command'], arglist, ret_type, middle_mode)) - - ret = gen_marshal_input(cmd['command'], arglist, ret_type, middle_mode) + "\n" - fdef.write(ret) + prefix=prefix)) -if not middle_mode: - ret = gen_registry(commands) - fdef.write(ret) +schema = QAPISchema(input_file) +gen = QAPISchemaGenCommandVisitor() +schema.visit(gen) +fdef.write(gen.defn) +fdecl.write(gen.decl) close_output(fdef, fdecl) diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py index 56bc602a6d..d15fad98f3 100644 --- a/scripts/qapi-event.py +++ b/scripts/qapi-event.py @@ -2,78 +2,64 @@ # QAPI event generator # # Copyright (c) 2014 Wenchao Xia +# Copyright (c) 2015 Red Hat Inc. # # Authors: # Wenchao Xia <wenchaoqemu@gmail.com> +# Markus Armbruster <armbru@redhat.com> # # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. -from ordereddict import OrderedDict from qapi import * -def _generate_event_api_name(event_name, params): - api_name = "void qapi_event_send_%s(" % c_name(event_name).lower(); - l = len(api_name) - if params: - for argname, argentry, optional in parse_args(params): - if optional: - api_name += "bool has_%s,\n" % c_name(argname) - api_name += "".ljust(l) +def gen_event_send_proto(name, arg_type): + return 'void qapi_event_send_%(c_name)s(%(param)s)' % { + 'c_name': c_name(name.lower()), + 'param': gen_params(arg_type, 'Error **errp')} - api_name += "%s %s,\n" % (c_type(argentry, is_param=True), - c_name(argname)) - api_name += "".ljust(l) - api_name += "Error **errp)" - return api_name; - - -# Following are the core functions that generate C APIs to emit event. - -def generate_event_declaration(api_name): +def gen_event_send_decl(name, arg_type): return mcgen(''' -%(api_name)s; +%(proto)s; ''', - api_name = api_name) + proto=gen_event_send_proto(name, arg_type)) -def generate_event_implement(api_name, event_name, params): - # step 1: declare any variables - ret = mcgen(""" -%(api_name)s +def gen_event_send(name, arg_type): + ret = mcgen(''' + +%(proto)s { QDict *qmp; Error *local_err = NULL; QMPEventFuncEmit emit; -""", - api_name = api_name) +''', + proto=gen_event_send_proto(name, arg_type)) - if params: - ret += mcgen(""" + if arg_type and arg_type.members: + ret += mcgen(''' QmpOutputVisitor *qov; Visitor *v; QObject *obj; -""") +''') - # step 2: check emit function, create a dict - ret += mcgen(""" + ret += mcgen(''' emit = qmp_event_get_func_emit(); if (!emit) { return; } - qmp = qmp_event_build_dict("%(event_name)s"); + qmp = qmp_event_build_dict("%(name)s"); -""", - event_name = event_name) +''', + name=name) - # step 3: visit the params if params != None - if params: - ret += mcgen(""" + if arg_type and arg_type.members: + ret += mcgen(''' qov = qmp_output_visitor_new(); g_assert(qov); @@ -81,45 +67,46 @@ def generate_event_implement(api_name, event_name, params): g_assert(v); /* Fake visit, as if all members are under a structure */ - visit_start_struct(v, NULL, "", "%(event_name)s", 0, &local_err); + visit_start_struct(v, NULL, "", "%(name)s", 0, &local_err); if (local_err) { goto clean; } -""", - event_name = event_name) +''', + name=name) - for argname, argentry, optional in parse_args(params): - if optional: - ret += mcgen(""" - if (has_%(var)s) { -""", - var = c_name(argname)) + for memb in arg_type.members: + if memb.optional: + ret += mcgen(''' + if (has_%(c_name)s) { +''', + c_name=c_name(memb.name)) push_indent() - if argentry == "str": - var_type = "(char **)" + # Ugly: need to cast away the const + if memb.type.name == "str": + cast = '(char **)' else: - var_type = "" + cast = '' - ret += mcgen(""" - visit_type_%(type)s(v, %(var_type)s&%(var)s, "%(name)s", &local_err); + ret += mcgen(''' + visit_type_%(c_type)s(v, %(cast)s&%(c_name)s, "%(name)s", &local_err); if (local_err) { goto clean; } -""", - var_type = var_type, - var = c_name(argname), - type = type_name(argentry), - name = argname) +''', + cast=cast, + c_name=c_name(memb.name), + c_type=memb.type.c_name(), + name=memb.name) - if optional: + if memb.optional: pop_indent() - ret += mcgen(""" + ret += mcgen(''' } -""") +''') - ret += mcgen(""" + ret += mcgen(''' visit_end_struct(v, &local_err); if (local_err) { @@ -130,87 +117,48 @@ def generate_event_implement(api_name, event_name, params): g_assert(obj != NULL); qdict_put_obj(qmp, "data", obj); -""") +''') - # step 4: call qmp event api - ret += mcgen(""" - emit(%(event_enum_value)s, qmp, &local_err); + ret += mcgen(''' + emit(%(c_enum)s, qmp, &local_err); -""", - event_enum_value = event_enum_value) +''', + c_enum=c_enum_const(event_enum_name, name)) - # step 5: clean up - if params: - ret += mcgen(""" + if arg_type and arg_type.members: + ret += mcgen(''' clean: qmp_output_visitor_cleanup(qov); -""") - ret += mcgen(""" +''') + ret += mcgen(''' error_propagate(errp, local_err); QDECREF(qmp); } -""") - +''') return ret -# Following are the functions that generate an enum type for all defined -# events, similar to qapi-types.py. Here we already have enum name and -# values which were generated before and recorded in event_enum_*. It also -# works around the issue that "import qapi-types" can't work. +class QAPISchemaGenEventVisitor(QAPISchemaVisitor): + def __init__(self): + self.decl = None + self.defn = None + self._event_names = None -def generate_event_enum_decl(event_enum_name, event_enum_values): - lookup_decl = mcgen(''' + def visit_begin(self, schema): + self.decl = '' + self.defn = '' + self._event_names = [] -extern const char *%(event_enum_name)s_lookup[]; -''', - event_enum_name = event_enum_name) - - enum_decl = mcgen(''' -typedef enum %(event_enum_name)s -{ -''', - event_enum_name = event_enum_name) - - # append automatically generated _MAX value - enum_max_value = c_enum_const(event_enum_name, "MAX") - enum_values = event_enum_values + [ enum_max_value ] - - i = 0 - for value in enum_values: - enum_decl += mcgen(''' - %(value)s = %(i)d, -''', - value = value, - i = i) - i += 1 - - enum_decl += mcgen(''' -} %(event_enum_name)s; -''', - event_enum_name = event_enum_name) - - return lookup_decl + enum_decl - -def generate_event_enum_lookup(event_enum_name, event_enum_strings): - ret = mcgen(''' + def visit_end(self): + self.decl += gen_enum(event_enum_name, self._event_names) + self.defn += gen_enum_lookup(event_enum_name, self._event_names) + self._event_names = None -const char *%(event_enum_name)s_lookup[] = { -''', - event_enum_name = event_enum_name) - - i = 0 - for string in event_enum_strings: - ret += mcgen(''' - "%(string)s", -''', - string = string) + def visit_event(self, name, info, arg_type): + self.decl += gen_event_send_decl(name, arg_type) + self.defn += gen_event_send(name, arg_type) + self._event_names.append(name) - ret += mcgen(''' - NULL, -}; -''') - return ret (input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line() @@ -265,35 +213,12 @@ fdecl.write(mcgen(''' ''', prefix=prefix)) -exprs = parse_schema(input_file) - -event_enum_name = prefix.upper().replace('-', '_') + "QAPIEvent" -event_enum_values = [] -event_enum_strings = [] - -for expr in exprs: - if expr.has_key('event'): - event_name = expr['event'] - params = expr.get('data') - if params and len(params) == 0: - params = None - - api_name = _generate_event_api_name(event_name, params) - ret = generate_event_declaration(api_name) - fdecl.write(ret) - - # We need an enum value per event - event_enum_value = c_enum_const(event_enum_name, event_name) - ret = generate_event_implement(api_name, event_name, params) - fdef.write(ret) - - # Record it, and generate enum later - event_enum_values.append(event_enum_value) - event_enum_strings.append(event_name) +event_enum_name = c_name(prefix + "QAPIEvent", protect=False) -ret = generate_event_enum_decl(event_enum_name, event_enum_values) -fdecl.write(ret) -ret = generate_event_enum_lookup(event_enum_name, event_enum_strings) -fdef.write(ret) +schema = QAPISchema(input_file) +gen = QAPISchemaGenEventVisitor() +schema.visit(gen) +fdef.write(gen.defn) +fdecl.write(gen.decl) close_output(fdef, fdecl) diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py new file mode 100644 index 0000000000..7d39320174 --- /dev/null +++ b/scripts/qapi-introspect.py @@ -0,0 +1,213 @@ +# +# QAPI introspection generator +# +# Copyright (C) 2015 Red Hat, Inc. +# +# Authors: +# Markus Armbruster <armbru@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. + +from qapi import * + + +# Caveman's json.dumps() replacement (we're stuck at Python 2.4) +# TODO try to use json.dumps() once we get unstuck +def to_json(obj, level=0): + if obj is None: + ret = 'null' + elif isinstance(obj, str): + ret = '"' + obj.replace('"', r'\"') + '"' + elif isinstance(obj, list): + elts = [to_json(elt, level + 1) + for elt in obj] + ret = '[' + ', '.join(elts) + ']' + elif isinstance(obj, dict): + elts = ['"%s": %s' % (key.replace('"', r'\"'), + to_json(obj[key], level + 1)) + for key in sorted(obj.keys())] + ret = '{' + ', '.join(elts) + '}' + else: + assert False # not implemented + if level == 1: + ret = '\n' + ret + return ret + + +def to_c_string(string): + return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"' + + +class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor): + def __init__(self, unmask): + self._unmask = unmask + self.defn = None + self.decl = None + self._schema = None + self._jsons = None + self._used_types = None + self._name_map = None + + def visit_begin(self, schema): + self._schema = schema + self._jsons = [] + self._used_types = [] + self._name_map = {} + return QAPISchemaType # don't visit types for now + + def visit_end(self): + # visit the types that are actually used + jsons = self._jsons + self._jsons = [] + for typ in self._used_types: + typ.visit(self) + # generate C + # TODO can generate awfully long lines + jsons.extend(self._jsons) + name = prefix + 'qmp_schema_json' + self.decl = mcgen(''' +extern const char %(c_name)s[]; +''', + c_name=c_name(name)) + lines = to_json(jsons).split('\n') + c_string = '\n '.join([to_c_string(line) for line in lines]) + self.defn = mcgen(''' +const char %(c_name)s[] = %(c_string)s; +''', + c_name=c_name(name), + c_string=c_string) + self._schema = None + self._jsons = None + self._used_types = None + self._name_map = None + + def _name(self, name): + if self._unmask: + return name + if name not in self._name_map: + self._name_map[name] = '%d' % len(self._name_map) + return self._name_map[name] + + def _use_type(self, typ): + # Map the various integer types to plain int + if typ.json_type() == 'int': + typ = self._schema.lookup_type('int') + elif (isinstance(typ, QAPISchemaArrayType) and + typ.element_type.json_type() == 'int'): + typ = self._schema.lookup_type('intList') + # Add type to work queue if new + if typ not in self._used_types: + self._used_types.append(typ) + # Clients should examine commands and events, not types. Hide + # type names to reduce the temptation. Also saves a few + # characters. + if isinstance(typ, QAPISchemaBuiltinType): + return typ.name + return self._name(typ.name) + + def _gen_json(self, name, mtype, obj): + if mtype != 'command' and mtype != 'event' and mtype != 'builtin': + name = self._name(name) + obj['name'] = name + obj['meta-type'] = mtype + self._jsons.append(obj) + + def _gen_member(self, member): + ret = {'name': member.name, 'type': self._use_type(member.type)} + if member.optional: + ret['default'] = None + return ret + + def _gen_variants(self, tag_name, variants): + return {'tag': tag_name, + 'variants': [self._gen_variant(v) for v in variants]} + + def _gen_variant(self, variant): + return {'case': variant.name, 'type': self._use_type(variant.type)} + + def visit_builtin_type(self, name, info, json_type): + self._gen_json(name, 'builtin', {'json-type': json_type}) + + def visit_enum_type(self, name, info, values, prefix): + self._gen_json(name, 'enum', {'values': values}) + + def visit_array_type(self, name, info, element_type): + self._gen_json(name, 'array', + {'element-type': self._use_type(element_type)}) + + def visit_object_type_flat(self, name, info, members, variants): + obj = {'members': [self._gen_member(m) for m in members]} + if variants: + obj.update(self._gen_variants(variants.tag_member.name, + variants.variants)) + self._gen_json(name, 'object', obj) + + def visit_alternate_type(self, name, info, variants): + self._gen_json(name, 'alternate', + {'members': [{'type': self._use_type(m.type)} + for m in variants.variants]}) + + def visit_command(self, name, info, arg_type, ret_type, + gen, success_response): + arg_type = arg_type or self._schema.the_empty_object_type + ret_type = ret_type or self._schema.the_empty_object_type + self._gen_json(name, 'command', + {'arg-type': self._use_type(arg_type), + 'ret-type': self._use_type(ret_type)}) + + def visit_event(self, name, info, arg_type): + arg_type = arg_type or self._schema.the_empty_object_type + self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)}) + +# Debugging aid: unmask QAPI schema's type names +# We normally mask them, because they're not QMP wire ABI +opt_unmask = False + +(input_file, output_dir, do_c, do_h, prefix, opts) = \ + parse_command_line("u", ["unmask-non-abi-names"]) + +for o, a in opts: + if o in ("-u", "--unmask-non-abi-names"): + opt_unmask = True + +c_comment = ''' +/* + * QAPI/QMP schema introspection + * + * Copyright (C) 2015 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ +''' +h_comment = ''' +/* + * QAPI/QMP schema introspection + * + * Copyright (C) 2015 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ +''' + +(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix, + 'qmp-introspect.c', 'qmp-introspect.h', + c_comment, h_comment) + +fdef.write(mcgen(''' +#include "%(prefix)sqmp-introspect.h" + +''', + prefix=prefix)) + +schema = QAPISchema(input_file) +gen = QAPISchemaGenIntrospectVisitor(opt_unmask) +schema.visit(gen) +fdef.write(gen.defn) +fdecl.write(gen.decl) + +close_output(fdef, fdecl) diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index e6eb4b613a..b292682df6 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -2,98 +2,81 @@ # QAPI types generator # # Copyright IBM, Corp. 2011 +# Copyright (c) 2013-2015 Red Hat Inc. # # Authors: # Anthony Liguori <aliguori@us.ibm.com> +# Markus Armbruster <armbru@redhat.com> # # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. -from ordereddict import OrderedDict from qapi import * -def generate_fwd_builtin(name): + +def gen_fwd_object_or_array(name): return mcgen(''' -typedef struct %(name)sList -{ - union { - %(type)s value; - uint64_t padding; - }; - struct %(name)sList *next; -} %(name)sList; +typedef struct %(c_name)s %(c_name)s; ''', - type=c_type(name), - name=name) + c_name=c_name(name)) -def generate_fwd_struct(name): - return mcgen(''' -typedef struct %(name)s %(name)s; +def gen_array(name, element_type): + return mcgen(''' -typedef struct %(name)sList -{ +struct %(c_name)s { union { - %(name)s *value; + %(c_type)s value; uint64_t padding; }; - struct %(name)sList *next; -} %(name)sList; + %(c_name)s *next; +}; ''', - name=c_name(name)) + c_name=c_name(name), c_type=element_type.c_type()) -def generate_fwd_enum_struct(name): - return mcgen(''' -typedef struct %(name)sList -{ - union { - %(name)s value; - uint64_t padding; - }; - struct %(name)sList *next; -} %(name)sList; -''', - name=c_name(name)) -def generate_struct_fields(members): +def gen_struct_field(name, typ, optional): ret = '' - for argname, argentry, optional in parse_args(members): - if optional: - ret += mcgen(''' + if optional: + ret += mcgen(''' bool has_%(c_name)s; ''', - c_name=c_name(argname)) - ret += mcgen(''' + c_name=c_name(name)) + ret += mcgen(''' %(c_type)s %(c_name)s; ''', - c_type=c_type(argentry), c_name=c_name(argname)) - + c_type=typ.c_type(), c_name=c_name(name)) return ret -def generate_struct(expr): - structname = expr.get('struct', "") - members = expr['data'] - base = expr.get('base') +def gen_struct_fields(members): + ret = '' + for memb in members: + ret += gen_struct_field(memb.name, memb.type, memb.optional) + return ret + + +def gen_struct(name, base, members): ret = mcgen(''' -struct %(name)s -{ + +struct %(c_name)s { ''', - name=c_name(structname)) + c_name=c_name(name)) if base: - ret += generate_struct_fields({'base': base}) + ret += gen_struct_field('base', base, False) - ret += generate_struct_fields(members) + ret += gen_struct_fields(members) # Make sure that all structs have at least one field; this avoids - # potential issues with attempting to malloc space for zero-length structs - # in C, and also incompatibility with C++ (where an empty struct is size 1). + # potential issues with attempting to malloc space for zero-length + # structs in C, and also incompatibility with C++ (where an empty + # struct is size 1). if not base and not members: - ret += mcgen(''' + ret += mcgen(''' char qapi_dummy_field_for_empty_struct; ''') @@ -103,80 +86,32 @@ struct %(name)s return ret -def generate_enum_lookup(name, values): - ret = mcgen(''' -const char * const %(name)s_lookup[] = { -''', - name=c_name(name)) - i = 0 - for value in values: - index = c_enum_const(name, value) - ret += mcgen(''' - [%(index)s] = "%(value)s", -''', - index = index, value = value) - - max_index = c_enum_const(name, 'MAX') - ret += mcgen(''' - [%(max_index)s] = NULL, -}; - -''', - max_index=max_index) - return ret - -def generate_enum(name, values): - name = c_name(name) - lookup_decl = mcgen(''' -extern const char * const %(name)s_lookup[]; -''', - name=name) - - enum_decl = mcgen(''' -typedef enum %(name)s -{ -''', - name=name) - # append automatically generated _MAX value - enum_values = values + [ 'MAX' ] - - i = 0 - for value in enum_values: - enum_full_value = c_enum_const(name, value) - enum_decl += mcgen(''' - %(enum_full_value)s = %(i)d, -''', - enum_full_value = enum_full_value, - i=i) - i += 1 +def gen_alternate_qtypes_decl(name): + return mcgen(''' - enum_decl += mcgen(''' -} %(name)s; +extern const int %(c_name)s_qtypes[]; ''', - name=name) - - return lookup_decl + enum_decl - -def generate_alternate_qtypes(expr): + c_name=c_name(name)) - name = expr['alternate'] - members = expr['data'] +def gen_alternate_qtypes(name, variants): ret = mcgen(''' -const int %(name)s_qtypes[QTYPE_MAX] = { + +const int %(c_name)s_qtypes[QTYPE_MAX] = { ''', - name=c_name(name)) + c_name=c_name(name)) - for key in members: - qtype = find_alternate_member_qtype(members[key]) - assert qtype, "Invalid alternate member" + for var in variants.variants: + qtype = var.type.alternate_qtype() + assert qtype ret += mcgen(''' [%(qtype)s] = %(enum_const)s, ''', - qtype = qtype, - enum_const = c_enum_const(name + 'Kind', key)) + qtype=qtype, + enum_const=c_enum_const(variants.tag_member.type.name, + var.name)) ret += mcgen(''' }; @@ -184,72 +119,74 @@ const int %(name)s_qtypes[QTYPE_MAX] = { return ret -def generate_union(expr, meta): - - name = c_name(expr[meta]) - typeinfo = expr['data'] - - base = expr.get('base') - discriminator = expr.get('discriminator') +def gen_union(name, base, variants): + ret = mcgen(''' - enum_define = discriminator_find_enum_define(expr) - if enum_define: - discriminator_type_name = enum_define['enum_name'] +struct %(c_name)s { +''', + c_name=c_name(name)) + if base: + ret += mcgen(''' + /* Members inherited from %(c_name)s: */ +''', + c_name=c_name(base.name)) + ret += gen_struct_fields(base.members) + ret += mcgen(''' + /* Own members: */ +''') else: - discriminator_type_name = '%sKind' % (name) - - ret = mcgen(''' -struct %(name)s -{ - %(discriminator_type_name)s kind; - union { + ret += mcgen(''' + %(c_type)s kind; +''', + c_type=c_name(variants.tag_member.type.name)) + + # FIXME: What purpose does data serve, besides preventing a union that + # has a branch named 'data'? We use it in qapi-visit.py to decide + # whether to bypass the switch statement if visiting the discriminator + # failed; but since we 0-initialize structs, and cannot tell what + # branch of the union is in use if the discriminator is invalid, there + # should not be any data leaks even without a data pointer. Or, if + # 'data' is merely added to guarantee we don't have an empty union, + # shouldn't we enforce that at .json parse time? + ret += mcgen(''' + union { /* union tag is @%(c_name)s */ void *data; ''', - name=name, - discriminator_type_name=c_name(discriminator_type_name)) - - for key in typeinfo: + # TODO ugly special case for simple union + # Use same tag name in C as on the wire to get rid of + # it, then: c_name=c_name(variants.tag_member.name) + c_name=c_name(variants.tag_name or 'kind')) + + for var in variants.variants: + # Ugly special case for simple union TODO get rid of it + typ = var.simple_union_type() or var.type ret += mcgen(''' %(c_type)s %(c_name)s; ''', - c_type=c_type(typeinfo[key]), - c_name=c_name(key)) + c_type=typ.c_type(), + c_name=c_name(var.name)) ret += mcgen(''' }; -''') - - if base: - assert discriminator - base_fields = find_struct(base)['data'].copy() - del base_fields[discriminator] - ret += generate_struct_fields(base_fields) - else: - assert not discriminator - - ret += mcgen(''' }; ''') - if meta == 'alternate': - ret += mcgen(''' -extern const int %(name)s_qtypes[]; -''', - name=name) - return ret -def generate_type_cleanup_decl(name): + +def gen_type_cleanup_decl(name): ret = mcgen(''' -void qapi_free_%(name)s(%(c_type)s obj); + +void qapi_free_%(c_name)s(%(c_name)s *obj); ''', - c_type=c_type(name), name=c_name(name)) + c_name=c_name(name)) return ret -def generate_type_cleanup(name): + +def gen_type_cleanup(name): ret = mcgen(''' -void qapi_free_%(name)s(%(c_type)s obj) +void qapi_free_%(c_name)s(%(c_name)s *obj) { QapiDeallocVisitor *md; Visitor *v; @@ -260,13 +197,83 @@ void qapi_free_%(name)s(%(c_type)s obj) md = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(md); - visit_type_%(name)s(v, &obj, NULL, NULL); + visit_type_%(c_name)s(v, &obj, NULL, NULL); qapi_dealloc_visitor_cleanup(md); } ''', - c_type=c_type(name), name=c_name(name)) + c_name=c_name(name)) return ret + +class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): + def __init__(self): + self.decl = None + self.defn = None + self._fwdecl = None + self._fwdefn = None + self._btin = None + + def visit_begin(self, schema): + self.decl = '' + self.defn = '' + self._fwdecl = '' + self._fwdefn = '' + self._btin = guardstart('QAPI_TYPES_BUILTIN') + + def visit_end(self): + self.decl = self._fwdecl + self.decl + self._fwdecl = None + self.defn = self._fwdefn + self.defn + self._fwdefn = None + # To avoid header dependency hell, we always generate + # declarations for built-in types in our header files and + # simply guard them. See also do_builtins (command line + # option -b). + self._btin += guardend('QAPI_TYPES_BUILTIN') + self.decl = self._btin + self.decl + self._btin = None + + def _gen_type_cleanup(self, name): + self.decl += gen_type_cleanup_decl(name) + self.defn += gen_type_cleanup(name) + + def visit_enum_type(self, name, info, values, prefix): + self._fwdecl += gen_enum(name, values, prefix) + self._fwdefn += gen_enum_lookup(name, values, prefix) + + def visit_array_type(self, name, info, element_type): + if isinstance(element_type, QAPISchemaBuiltinType): + self._btin += gen_fwd_object_or_array(name) + self._btin += gen_array(name, element_type) + self._btin += gen_type_cleanup_decl(name) + if do_builtins: + self.defn += gen_type_cleanup(name) + else: + self._fwdecl += gen_fwd_object_or_array(name) + self.decl += gen_array(name, element_type) + self._gen_type_cleanup(name) + + def visit_object_type(self, name, info, base, members, variants): + if info: + self._fwdecl += gen_fwd_object_or_array(name) + if variants: + assert not members # not implemented + self.decl += gen_union(name, base, variants) + else: + self.decl += gen_struct(name, base, members) + self._gen_type_cleanup(name) + + def visit_alternate_type(self, name, info, variants): + self._fwdecl += gen_fwd_object_or_array(name) + self._fwdefn += gen_alternate_qtypes(name, variants) + self.decl += gen_union(name, None, variants) + self.decl += gen_alternate_qtypes_decl(name) + self._gen_type_cleanup(name) + +# If you link code generated from multiple schemata, you want only one +# instance of the code for built-in types. Generate it only when +# do_builtins, enabled by command line option -b. See also +# QAPISchemaGenTypeVisitor.visit_end(). do_builtins = False (input_file, output_dir, do_c, do_h, prefix, opts) = \ @@ -314,89 +321,19 @@ fdef.write(mcgen(''' #include "qapi/dealloc-visitor.h" #include "%(prefix)sqapi-types.h" #include "%(prefix)sqapi-visit.h" - ''', prefix=prefix)) fdecl.write(mcgen(''' #include <stdbool.h> #include <stdint.h> - +#include "qapi/qmp/qobject.h" ''')) -exprs = parse_schema(input_file) - -fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL")) -for typename in builtin_types.keys(): - fdecl.write(generate_fwd_builtin(typename)) -fdecl.write(guardend("QAPI_TYPES_BUILTIN_STRUCT_DECL")) - -for expr in exprs: - ret = "\n" - if expr.has_key('struct'): - ret += generate_fwd_struct(expr['struct']) - elif expr.has_key('enum'): - ret += generate_enum(expr['enum'], expr['data']) + "\n" - ret += generate_fwd_enum_struct(expr['enum']) - fdef.write(generate_enum_lookup(expr['enum'], expr['data'])) - elif expr.has_key('union'): - ret += generate_fwd_struct(expr['union']) + "\n" - enum_define = discriminator_find_enum_define(expr) - if not enum_define: - ret += generate_enum('%sKind' % expr['union'], expr['data'].keys()) - fdef.write(generate_enum_lookup('%sKind' % expr['union'], - expr['data'].keys())) - elif expr.has_key('alternate'): - ret += generate_fwd_struct(expr['alternate']) + "\n" - ret += generate_enum('%sKind' % expr['alternate'], expr['data'].keys()) - fdef.write(generate_enum_lookup('%sKind' % expr['alternate'], - expr['data'].keys())) - fdef.write(generate_alternate_qtypes(expr)) - else: - continue - fdecl.write(ret) - -# to avoid header dependency hell, we always generate declarations -# for built-in types in our header files and simply guard them -fdecl.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DECL")) -for typename in builtin_types.keys(): - fdecl.write(generate_type_cleanup_decl(typename + "List")) -fdecl.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DECL")) - -# ...this doesn't work for cases where we link in multiple objects that -# have the functions defined, so we use -b option to provide control -# over these cases -if do_builtins: - fdef.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DEF")) - for typename in builtin_types.keys(): - fdef.write(generate_type_cleanup(typename + "List")) - fdef.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DEF")) - -for expr in exprs: - ret = "\n" - if expr.has_key('struct'): - ret += generate_struct(expr) + "\n" - ret += generate_type_cleanup_decl(expr['struct'] + "List") - fdef.write(generate_type_cleanup(expr['struct'] + "List") + "\n") - ret += generate_type_cleanup_decl(expr['struct']) - fdef.write(generate_type_cleanup(expr['struct']) + "\n") - elif expr.has_key('union'): - ret += generate_union(expr, 'union') - ret += generate_type_cleanup_decl(expr['union'] + "List") - fdef.write(generate_type_cleanup(expr['union'] + "List") + "\n") - ret += generate_type_cleanup_decl(expr['union']) - fdef.write(generate_type_cleanup(expr['union']) + "\n") - elif expr.has_key('alternate'): - ret += generate_union(expr, 'alternate') - ret += generate_type_cleanup_decl(expr['alternate'] + "List") - fdef.write(generate_type_cleanup(expr['alternate'] + "List") + "\n") - ret += generate_type_cleanup_decl(expr['alternate']) - fdef.write(generate_type_cleanup(expr['alternate']) + "\n") - elif expr.has_key('enum'): - ret += generate_type_cleanup_decl(expr['enum'] + "List") - fdef.write(generate_type_cleanup(expr['enum'] + "List") + "\n") - else: - continue - fdecl.write(ret) +schema = QAPISchema(input_file) +gen = QAPISchemaGenTypeVisitor() +schema.visit(gen) +fdef.write(gen.defn) +fdecl.write(gen.decl) close_output(fdef, fdecl) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 5b99336488..97343cf7e9 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -12,18 +12,38 @@ # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. -from ordereddict import OrderedDict from qapi import * import re -implicit_structs = [] +implicit_structs_seen = set() +struct_fields_seen = set() -def generate_visit_implicit_struct(type): - global implicit_structs - if type in implicit_structs: - return '' - implicit_structs.append(type) + +def gen_visit_decl(name, scalar=False): + c_type = c_name(name) + ' *' + if not scalar: + c_type += '*' return mcgen(''' +void visit_type_%(c_name)s(Visitor *m, %(c_type)sobj, const char *name, Error **errp); +''', + c_name=c_name(name), c_type=c_type) + + +def gen_visit_implicit_struct(typ): + if typ in implicit_structs_seen: + return '' + implicit_structs_seen.add(typ) + + ret = '' + if typ.name not in struct_fields_seen: + # Need a forward declaration + ret += mcgen(''' + +static void visit_type_%(c_type)s_fields(Visitor *m, %(c_type)s **obj, Error **errp); +''', + c_type=typ.c_name()) + + ret += mcgen(''' static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp) { @@ -37,49 +57,53 @@ static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error * error_propagate(errp, err); } ''', - c_type=type_name(type)) + c_type=typ.c_name()) + return ret + + +def gen_visit_struct_fields(name, base, members): + struct_fields_seen.add(name) -def generate_visit_struct_fields(name, members, base = None): - substructs = [] ret = '' if base: - ret += generate_visit_implicit_struct(base) + ret += gen_visit_implicit_struct(base) ret += mcgen(''' -static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp) +static void visit_type_%(c_name)s_fields(Visitor *m, %(c_name)s **obj, Error **errp) { Error *err = NULL; + ''', - name=c_name(name)) + c_name=c_name(name)) push_indent() if base: ret += mcgen(''' -visit_type_implicit_%(type)s(m, &(*obj)->%(c_name)s, &err); +visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err); if (err) { goto out; } ''', - type=type_name(base), c_name=c_name('base')) + c_type=base.c_name(), c_name=c_name('base')) - for argname, argentry, optional in parse_args(members): - if optional: + for memb in members: + if memb.optional: ret += mcgen(''' visit_optional(m, &(*obj)->has_%(c_name)s, "%(name)s", &err); if (!err && (*obj)->has_%(c_name)s) { ''', - c_name=c_name(argname), name=argname) + c_name=c_name(memb.name), name=memb.name) push_indent() ret += mcgen(''' -visit_type_%(type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err); +visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err); ''', - type=type_name(argentry), c_name=c_name(argname), - name=argname) + c_type=memb.type.c_name(), c_name=c_name(memb.name), + name=memb.name) - if optional: + if memb.optional: pop_indent() ret += mcgen(''' } @@ -91,7 +115,7 @@ if (err) { ''') pop_indent() - if re.search('^ *goto out\\;', ret, re.MULTILINE): + if re.search('^ *goto out;', ret, re.MULTILINE): ret += mcgen(''' out: @@ -103,8 +127,17 @@ out: return ret -def generate_visit_struct_body(name, members): - ret = mcgen(''' +def gen_visit_struct(name, base, members): + ret = gen_visit_struct_fields(name, base, members) + + # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to + # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj + # rather than leaving it non-NULL. As currently written, the caller must + # call qapi_free_FOO() to avoid a memory leak of the partial FOO. + ret += mcgen(''' + +void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp) +{ Error *err = NULL; visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err); @@ -115,37 +148,17 @@ def generate_visit_struct_body(name, members): visit_end_struct(m, &err); } error_propagate(errp, err); +} ''', - name=name, c_name=c_name(name)) + name=name, c_name=c_name(name)) return ret -def generate_visit_struct(expr): - - name = expr['struct'] - members = expr['data'] - base = expr.get('base') - - ret = generate_visit_struct_fields(name, members, base) - - ret += mcgen(''' - -void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) -{ -''', - name=c_name(name)) - - ret += generate_visit_struct_body(name, members) - ret += mcgen(''' -} -''') - return ret - -def generate_visit_list(name, members): +def gen_visit_list(name, element_type): return mcgen(''' -void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp) +void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp) { Error *err = NULL; GenericList *i, **prev; @@ -158,8 +171,8 @@ void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, E for (prev = (GenericList **)obj; !err && (i = visit_next_list(m, prev, &err)) != NULL; prev = &i) { - %(name)sList *native_i = (%(name)sList *)i; - visit_type_%(name)s(m, &native_i->value, NULL, &err); + %(c_name)s *native_i = (%(c_name)s *)i; + visit_type_%(c_elt_type)s(m, &native_i->value, NULL, &err); } error_propagate(errp, err); @@ -169,56 +182,49 @@ out: error_propagate(errp, err); } ''', - name=type_name(name)) + c_name=c_name(name), c_elt_type=element_type.c_name()) + -def generate_visit_enum(name, members): +def gen_visit_enum(name): return mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp) +void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error **errp) { - visit_type_enum(m, (int *)obj, %(name)s_lookup, "%(name)s", name, errp); + visit_type_enum(m, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp); } ''', - name=c_name(name)) + c_name=c_name(name), name=name) -def generate_visit_alternate(name, members): + +def gen_visit_alternate(name, variants): ret = mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) +void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp) { Error *err = NULL; - visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err); + visit_start_implicit_struct(m, (void**) obj, sizeof(%(c_name)s), &err); if (err) { goto out; } - visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err); + visit_get_next_type(m, (int*) &(*obj)->kind, %(c_name)s_qtypes, name, &err); if (err) { goto out_end; } switch ((*obj)->kind) { ''', - name=c_name(name)) - - # For alternate, always use the default enum type automatically generated - # as name + 'Kind' - disc_type = c_name(name) + 'Kind' - - for key in members: - assert (members[key] in builtin_types.keys() - or find_struct(members[key]) - or find_union(members[key]) - or find_enum(members[key])), "Invalid alternate member" + c_name=c_name(name)) - enum_full_value = c_enum_const(disc_type, key) + for var in variants.variants: ret += mcgen(''' - case %(enum_full_value)s: + case %(case)s: visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err); break; ''', - enum_full_value = enum_full_value, - c_type = type_name(members[key]), - c_name = c_name(key)) + case=c_enum_const(variants.tag_member.type.name, + var.name), + c_type=var.type.c_name(), + c_name=c_name(var.name)) ret += mcgen(''' default: @@ -236,90 +242,85 @@ out: return ret -def generate_visit_union(expr): - - name = expr['union'] - members = expr['data'] - - base = expr.get('base') - discriminator = expr.get('discriminator') - - enum_define = discriminator_find_enum_define(expr) - if enum_define: - # Use the enum type as discriminator - ret = "" - disc_type = c_name(enum_define['enum_name']) - else: - # There will always be a discriminator in the C switch code, by default - # it is an enum type generated silently - ret = generate_visit_enum(name + 'Kind', members.keys()) - disc_type = c_name(name) + 'Kind' +def gen_visit_union(name, base, variants): + ret = '' if base: - assert discriminator - base_fields = find_struct(base)['data'].copy() - del base_fields[discriminator] - ret += generate_visit_struct_fields(name, base_fields) + members = [m for m in base.members if m != variants.tag_member] + ret += gen_visit_struct_fields(name, None, members) - if discriminator: - for key in members: - ret += generate_visit_implicit_struct(members[key]) + for var in variants.variants: + # Ugly special case for simple union TODO get rid of it + if not var.simple_union_type(): + ret += gen_visit_implicit_struct(var.type) ret += mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) +void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp) { Error *err = NULL; - visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); + visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err); if (err) { goto out; } if (*obj) { ''', - name=c_name(name)) + c_name=c_name(name), name=name) if base: ret += mcgen(''' - visit_type_%(name)s_fields(m, obj, &err); + visit_type_%(c_name)s_fields(m, obj, &err); if (err) { goto out_obj; } ''', - name=c_name(name)) + c_name=c_name(name)) - if not discriminator: - disc_key = "type" - else: - disc_key = discriminator + tag_key = variants.tag_member.name + if not variants.tag_name: + # we pointlessly use a different key for simple unions + tag_key = 'type' ret += mcgen(''' - visit_type_%(disc_type)s(m, &(*obj)->kind, "%(disc_key)s", &err); + visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err); if (err) { goto out_obj; } if (!visit_start_union(m, !!(*obj)->data, &err) || err) { goto out_obj; } - switch ((*obj)->kind) { + switch ((*obj)->%(c_name)s) { ''', - disc_type = disc_type, - disc_key = disc_key) - - for key in members: - if not discriminator: - fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);' + c_type=variants.tag_member.type.c_name(), + # TODO ugly special case for simple union + # Use same tag name in C as on the wire to get rid of + # it, then: c_name=c_name(variants.tag_member.name) + c_name=c_name(variants.tag_name or 'kind'), + name=tag_key) + + for var in variants.variants: + # TODO ugly special case for simple union + simple_union_type = var.simple_union_type() + ret += mcgen(''' + case %(case)s: +''', + case=c_enum_const(variants.tag_member.type.name, + var.name)) + if simple_union_type: + ret += mcgen(''' + visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err); +''', + c_type=simple_union_type.c_name(), + c_name=c_name(var.name)) else: - fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);' - - enum_full_value = c_enum_const(disc_type, key) + ret += mcgen(''' + visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err); +''', + c_type=var.type.c_name(), + c_name=c_name(var.name)) ret += mcgen(''' - case %(enum_full_value)s: - ''' + fmt + ''' break; -''', - enum_full_value = enum_full_value, - c_type=type_name(members[key]), - c_name=c_name(key)) +''') ret += mcgen(''' default: @@ -340,38 +341,59 @@ out: return ret -def generate_declaration(name, members, builtin_type=False): - ret = "" - if not builtin_type: - name = c_name(name) - ret += mcgen(''' - -void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp); -''', - name=name) - - ret += mcgen(''' -void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp); -''', - name=name) - - return ret - -def generate_enum_declaration(name, members): - ret = mcgen(''' -void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp); -''', - name=c_name(name)) - - return ret - -def generate_decl_enum(name, members): - return mcgen(''' - -void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp); -''', - name=c_name(name)) +class QAPISchemaGenVisitVisitor(QAPISchemaVisitor): + def __init__(self): + self.decl = None + self.defn = None + self._btin = None + + def visit_begin(self, schema): + self.decl = '' + self.defn = '' + self._btin = guardstart('QAPI_VISIT_BUILTIN') + + def visit_end(self): + # To avoid header dependency hell, we always generate + # declarations for built-in types in our header files and + # simply guard them. See also do_builtins (command line + # option -b). + self._btin += guardend('QAPI_VISIT_BUILTIN') + self.decl = self._btin + self.decl + self._btin = None + + def visit_enum_type(self, name, info, values, prefix): + self.decl += gen_visit_decl(name, scalar=True) + self.defn += gen_visit_enum(name) + + def visit_array_type(self, name, info, element_type): + decl = gen_visit_decl(name) + defn = gen_visit_list(name, element_type) + if isinstance(element_type, QAPISchemaBuiltinType): + self._btin += decl + if do_builtins: + self.defn += defn + else: + self.decl += decl + self.defn += defn + + def visit_object_type(self, name, info, base, members, variants): + if info: + self.decl += gen_visit_decl(name) + if variants: + assert not members # not implemented + self.defn += gen_visit_union(name, base, variants) + else: + self.defn += gen_visit_struct(name, base, members) + + def visit_alternate_type(self, name, info, variants): + self.decl += gen_visit_decl(name) + self.defn += gen_visit_alternate(name, variants) + +# If you link code generated from multiple schemata, you want only one +# instance of the code for built-in types. Generate it only when +# do_builtins, enabled by command line option -b. See also +# QAPISchemaGenVisitVisitor.visit_end(). do_builtins = False (input_file, output_dir, do_c, do_h, prefix, opts) = \ @@ -418,7 +440,7 @@ fdef.write(mcgen(''' #include "qemu-common.h" #include "%(prefix)sqapi-visit.h" ''', - prefix = prefix)) + prefix=prefix)) fdecl.write(mcgen(''' #include "qapi/visitor.h" @@ -427,58 +449,10 @@ fdecl.write(mcgen(''' ''', prefix=prefix)) -exprs = parse_schema(input_file) - -# to avoid header dependency hell, we always generate declarations -# for built-in types in our header files and simply guard them -fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL")) -for typename in builtin_types.keys(): - fdecl.write(generate_declaration(typename, None, builtin_type=True)) -fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL")) - -# ...this doesn't work for cases where we link in multiple objects that -# have the functions defined, so we use -b option to provide control -# over these cases -if do_builtins: - for typename in builtin_types.keys(): - fdef.write(generate_visit_list(typename, None)) - -for expr in exprs: - if expr.has_key('struct'): - ret = generate_visit_struct(expr) - ret += generate_visit_list(expr['struct'], expr['data']) - fdef.write(ret) - - ret = generate_declaration(expr['struct'], expr['data']) - fdecl.write(ret) - elif expr.has_key('union'): - ret = generate_visit_union(expr) - ret += generate_visit_list(expr['union'], expr['data']) - fdef.write(ret) - - enum_define = discriminator_find_enum_define(expr) - ret = "" - if not enum_define: - ret = generate_decl_enum('%sKind' % expr['union'], - expr['data'].keys()) - ret += generate_declaration(expr['union'], expr['data']) - fdecl.write(ret) - elif expr.has_key('alternate'): - ret = generate_visit_alternate(expr['alternate'], expr['data']) - ret += generate_visit_list(expr['alternate'], expr['data']) - fdef.write(ret) - - ret = generate_decl_enum('%sKind' % expr['alternate'], - expr['data'].keys()) - ret += generate_declaration(expr['alternate'], expr['data']) - fdecl.write(ret) - elif expr.has_key('enum'): - ret = generate_visit_list(expr['enum'], expr['data']) - ret += generate_visit_enum(expr['enum'], expr['data']) - fdef.write(ret) - - ret = generate_decl_enum(expr['enum'], expr['data']) - ret += generate_enum_declaration(expr['enum'], expr['data']) - fdecl.write(ret) +schema = QAPISchema(input_file) +gen = QAPISchemaGenVisitVisitor() +schema.visit(gen) +fdef.write(gen.defn) +fdecl.write(gen.decl) close_output(fdef, fdecl) diff --git a/scripts/qapi.py b/scripts/qapi.py index 06d7fc2848..06478bb269 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -33,12 +33,14 @@ builtin_types = { 'uint32': 'QTYPE_QINT', 'uint64': 'QTYPE_QINT', 'size': 'QTYPE_QINT', + 'any': None, # any qtype_code possible, actually } # Whitelist of commands allowed to return a non-dictionary returns_whitelist = [ # From QMP: 'human-monitor-command', + 'qom-get', 'query-migrate-cache-size', 'query-tpm-models', 'query-tpm-types', @@ -103,7 +105,7 @@ class QAPIExprError(Exception): return error_path(self.info['parent']) + \ "%s:%d: %s" % (self.info['file'], self.info['line'], self.msg) -class QAPISchema: +class QAPISchemaParser(object): def __init__(self, fp, previously_included = [], incl_info = None): abs_fname = os.path.abspath(fp.name) @@ -149,8 +151,8 @@ class QAPISchema: except IOError, e: raise QAPIExprError(expr_info, '%s: %s' % (e.strerror, include)) - exprs_include = QAPISchema(fobj, previously_included, - expr_info) + exprs_include = QAPISchemaParser(fobj, previously_included, + expr_info) self.exprs.extend(exprs_include.exprs) else: expr_elem = {'expr': expr, @@ -302,6 +304,8 @@ class QAPISchema: # # Semantic analysis of schema expressions +# TODO fold into QAPISchema +# TODO catching name collisions in generated code would be nice # def find_base_fields(base): @@ -341,6 +345,8 @@ def discriminator_find_enum_define(expr): return find_enum(discriminator_type) +# FIXME should enforce "other than downstream extensions [...], all +# names should begin with a letter". valid_name = re.compile('^[a-zA-Z_][a-zA-Z0-9_.-]*$') def check_name(expr_info, source, name, allow_optional = False, enum_member = False): @@ -367,6 +373,8 @@ def check_name(expr_info, source, name, allow_optional = False, def add_name(name, info, meta, implicit = False): global all_names check_name(info, "'%s'" % meta, name) + # FIXME should reject names that differ only in '_' vs. '.' + # vs. '-', because they're liable to clash in generated C. if name in all_names: raise QAPIExprError(info, "%s '%s' is already defined" @@ -420,16 +428,12 @@ def is_enum(name): def check_type(expr_info, source, value, allow_array = False, allow_dict = False, allow_optional = False, - allow_star = False, allow_metas = []): + allow_metas = []): global all_names - orig_value = value if value is None: return - if allow_star and value == '**': - return - # Check if array type for value is okay if isinstance(value, list): if not allow_array: @@ -440,38 +444,35 @@ def check_type(expr_info, source, value, allow_array = False, "%s: array type must contain single type name" % source) value = value[0] - orig_value = "array of %s" %value # Check if type name for value is okay if isinstance(value, str): - if value == '**': - raise QAPIExprError(expr_info, - "%s uses '**' but did not request 'gen':false" - % source) if not value in all_names: raise QAPIExprError(expr_info, "%s uses unknown type '%s'" - % (source, orig_value)) + % (source, value)) if not all_names[value] in allow_metas: raise QAPIExprError(expr_info, "%s cannot use %s type '%s'" - % (source, all_names[value], orig_value)) + % (source, all_names[value], value)) return - # value is a dictionary, check that each member is okay - if not isinstance(value, OrderedDict): - raise QAPIExprError(expr_info, - "%s should be a dictionary" % source) if not allow_dict: raise QAPIExprError(expr_info, "%s should be a type name" % source) + + if not isinstance(value, OrderedDict): + raise QAPIExprError(expr_info, + "%s should be a dictionary or type name" % source) + + # value is a dictionary, check that each member is okay for (key, arg) in value.items(): check_name(expr_info, "Member of %s" % source, key, allow_optional=allow_optional) # Todo: allow dictionaries to represent default values of # an optional argument. check_type(expr_info, "Member '%s' of %s" % (key, source), arg, - allow_array=True, allow_star=allow_star, + allow_array=True, allow_metas=['built-in', 'union', 'alternate', 'struct', 'enum']) @@ -491,30 +492,27 @@ def check_member_clash(expr_info, base_name, data, source = ""): def check_command(expr, expr_info): name = expr['command'] - allow_star = expr.has_key('gen') check_type(expr_info, "'data' for command '%s'" % name, expr.get('data'), allow_dict=True, allow_optional=True, - allow_metas=['union', 'struct'], allow_star=allow_star) + allow_metas=['struct']) returns_meta = ['union', 'struct'] if name in returns_whitelist: returns_meta += ['built-in', 'alternate', 'enum'] check_type(expr_info, "'returns' for command '%s'" % name, - expr.get('returns'), allow_array=True, allow_dict=True, - allow_optional=True, allow_metas=returns_meta, - allow_star=allow_star) + expr.get('returns'), allow_array=True, + allow_optional=True, allow_metas=returns_meta) def check_event(expr, expr_info): global events name = expr['event'] - params = expr.get('data') if name.upper() == 'MAX': raise QAPIExprError(expr_info, "Event name 'MAX' cannot be created") events.append(name) check_type(expr_info, "'data' for event '%s'" % name, expr.get('data'), allow_dict=True, allow_optional=True, - allow_metas=['union', 'struct']) + allow_metas=['struct']) def check_union(expr, expr_info): name = expr['union'] @@ -523,14 +521,6 @@ def check_union(expr, expr_info): members = expr['data'] values = { 'MAX': '(automatic)' } - # If the object has a member 'base', its value must name a struct, - # and there must be a discriminator. - if base is not None: - if discriminator is None: - raise QAPIExprError(expr_info, - "Union '%s' requires a discriminator to go " - "along with base" %name) - # Two types of unions, determined by discriminator. # With no discriminator it is a simple union. @@ -639,11 +629,15 @@ def check_alternate(expr, expr_info): def check_enum(expr, expr_info): name = expr['enum'] members = expr.get('data') + prefix = expr.get('prefix') values = { 'MAX': '(automatic)' } if not isinstance(members, list): raise QAPIExprError(expr_info, "Enum '%s' requires an array for 'data'" % name) + if prefix is not None and not isinstance(prefix, str): + raise QAPIExprError(expr_info, + "Enum '%s' requires a string for 'prefix'" % name) for member in members: check_name(expr_info, "Member of enum '%s'" %name, member, enum_member=True) @@ -698,7 +692,7 @@ def check_exprs(exprs): expr = expr_elem['expr'] info = expr_elem['info'] if expr.has_key('enum'): - check_keys(expr_elem, 'enum', ['data']) + check_keys(expr_elem, 'enum', ['data'], ['prefix']) add_enum(expr['enum'], info, expr['data']) elif expr.has_key('union'): check_keys(expr_elem, 'union', ['data'], @@ -752,36 +746,538 @@ def check_exprs(exprs): else: assert False, 'unexpected meta type' - return map(lambda expr_elem: expr_elem['expr'], exprs) + return exprs -def parse_schema(fname): - try: - schema = QAPISchema(open(fname, "r")) - return check_exprs(schema.exprs) - except (QAPISchemaError, QAPIExprError), e: - print >>sys.stderr, e - exit(1) # -# Code generation helpers +# Schema compiler frontend # -def parse_args(typeinfo): - if isinstance(typeinfo, str): - struct = find_struct(typeinfo) - assert struct != None - typeinfo = struct['data'] +class QAPISchemaEntity(object): + def __init__(self, name, info): + assert isinstance(name, str) + self.name = name + self.info = info + + def c_name(self): + return c_name(self.name) + + def check(self, schema): + pass + + def visit(self, visitor): + pass + + +class QAPISchemaVisitor(object): + def visit_begin(self, schema): + pass + + def visit_end(self): + pass + + def visit_builtin_type(self, name, info, json_type): + pass + + def visit_enum_type(self, name, info, values, prefix): + pass + + def visit_array_type(self, name, info, element_type): + pass + + def visit_object_type(self, name, info, base, members, variants): + pass + + def visit_object_type_flat(self, name, info, members, variants): + pass + + def visit_alternate_type(self, name, info, variants): + pass + + def visit_command(self, name, info, arg_type, ret_type, + gen, success_response): + pass + + def visit_event(self, name, info, arg_type): + pass + + +class QAPISchemaType(QAPISchemaEntity): + def c_type(self, is_param=False): + return c_name(self.name) + pointer_suffix + + def c_null(self): + return 'NULL' + + def json_type(self): + pass + + def alternate_qtype(self): + json2qtype = { + 'string': 'QTYPE_QSTRING', + 'number': 'QTYPE_QFLOAT', + 'int': 'QTYPE_QINT', + 'boolean': 'QTYPE_QBOOL', + 'object': 'QTYPE_QDICT' + } + return json2qtype.get(self.json_type()) + + +class QAPISchemaBuiltinType(QAPISchemaType): + def __init__(self, name, json_type, c_type, c_null): + QAPISchemaType.__init__(self, name, None) + assert not c_type or isinstance(c_type, str) + assert json_type in ('string', 'number', 'int', 'boolean', 'null', + 'value') + self._json_type_name = json_type + self._c_type_name = c_type + self._c_null_val = c_null + + def c_name(self): + return self.name + + def c_type(self, is_param=False): + if is_param and self.name == 'str': + return 'const ' + self._c_type_name + return self._c_type_name + + def c_null(self): + return self._c_null_val + + def json_type(self): + return self._json_type_name + + def visit(self, visitor): + visitor.visit_builtin_type(self.name, self.info, self.json_type()) + + +class QAPISchemaEnumType(QAPISchemaType): + def __init__(self, name, info, values, prefix): + QAPISchemaType.__init__(self, name, info) + for v in values: + assert isinstance(v, str) + assert prefix is None or isinstance(prefix, str) + self.values = values + self.prefix = prefix + + def check(self, schema): + assert len(set(self.values)) == len(self.values) - for member in typeinfo: - argname = member - argentry = typeinfo[member] + def c_type(self, is_param=False): + return c_name(self.name) + + def c_null(self): + return c_enum_const(self.name, (self.values + ['MAX'])[0], + self.prefix) + + def json_type(self): + return 'string' + + def visit(self, visitor): + visitor.visit_enum_type(self.name, self.info, + self.values, self.prefix) + + +class QAPISchemaArrayType(QAPISchemaType): + def __init__(self, name, info, element_type): + QAPISchemaType.__init__(self, name, info) + assert isinstance(element_type, str) + self._element_type_name = element_type + self.element_type = None + + def check(self, schema): + self.element_type = schema.lookup_type(self._element_type_name) + assert self.element_type + + def json_type(self): + return 'array' + + def visit(self, visitor): + visitor.visit_array_type(self.name, self.info, self.element_type) + + +class QAPISchemaObjectType(QAPISchemaType): + def __init__(self, name, info, base, local_members, variants): + QAPISchemaType.__init__(self, name, info) + assert base is None or isinstance(base, str) + for m in local_members: + assert isinstance(m, QAPISchemaObjectTypeMember) + assert (variants is None or + isinstance(variants, QAPISchemaObjectTypeVariants)) + self._base_name = base + self.base = None + self.local_members = local_members + self.variants = variants + self.members = None + + def check(self, schema): + assert self.members is not False # not running in cycles + if self.members: + return + self.members = False # mark as being checked + if self._base_name: + self.base = schema.lookup_type(self._base_name) + assert isinstance(self.base, QAPISchemaObjectType) + assert not self.base.variants # not implemented + self.base.check(schema) + members = list(self.base.members) + else: + members = [] + seen = {} + for m in members: + seen[m.name] = m + for m in self.local_members: + m.check(schema, members, seen) + if self.variants: + self.variants.check(schema, members, seen) + self.members = members + + def c_name(self): + assert self.info + return QAPISchemaType.c_name(self) + + def c_type(self, is_param=False): + assert self.info + return QAPISchemaType.c_type(self) + + def json_type(self): + return 'object' + + def visit(self, visitor): + visitor.visit_object_type(self.name, self.info, + self.base, self.local_members, self.variants) + visitor.visit_object_type_flat(self.name, self.info, + self.members, self.variants) + + +class QAPISchemaObjectTypeMember(object): + def __init__(self, name, typ, optional): + assert isinstance(name, str) + assert isinstance(typ, str) + assert isinstance(optional, bool) + self.name = name + self._type_name = typ + self.type = None + self.optional = optional + + def check(self, schema, all_members, seen): + assert self.name not in seen + self.type = schema.lookup_type(self._type_name) + assert self.type + all_members.append(self) + seen[self.name] = self + + +class QAPISchemaObjectTypeVariants(object): + def __init__(self, tag_name, tag_enum, variants): + assert tag_name is None or isinstance(tag_name, str) + assert tag_enum is None or isinstance(tag_enum, str) + for v in variants: + assert isinstance(v, QAPISchemaObjectTypeVariant) + self.tag_name = tag_name + if tag_name: + assert not tag_enum + self.tag_member = None + else: + self.tag_member = QAPISchemaObjectTypeMember('type', tag_enum, + False) + self.variants = variants + + def check(self, schema, members, seen): + if self.tag_name: + self.tag_member = seen[self.tag_name] + else: + self.tag_member.check(schema, members, seen) + assert isinstance(self.tag_member.type, QAPISchemaEnumType) + for v in self.variants: + vseen = dict(seen) + v.check(schema, self.tag_member.type, vseen) + +class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): + def __init__(self, name, typ): + QAPISchemaObjectTypeMember.__init__(self, name, typ, False) + + def check(self, schema, tag_type, seen): + QAPISchemaObjectTypeMember.check(self, schema, [], seen) + assert self.name in tag_type.values + + # This function exists to support ugly simple union special cases + # TODO get rid of them, and drop the function + def simple_union_type(self): + if isinstance(self.type, QAPISchemaObjectType) and not self.type.info: + assert len(self.type.members) == 1 + assert not self.type.variants + return self.type.members[0].type + return None + + +class QAPISchemaAlternateType(QAPISchemaType): + def __init__(self, name, info, variants): + QAPISchemaType.__init__(self, name, info) + assert isinstance(variants, QAPISchemaObjectTypeVariants) + assert not variants.tag_name + self.variants = variants + + def check(self, schema): + self.variants.check(schema, [], {}) + + def json_type(self): + return 'value' + + def visit(self, visitor): + visitor.visit_alternate_type(self.name, self.info, self.variants) + + +class QAPISchemaCommand(QAPISchemaEntity): + def __init__(self, name, info, arg_type, ret_type, gen, success_response): + QAPISchemaEntity.__init__(self, name, info) + assert not arg_type or isinstance(arg_type, str) + assert not ret_type or isinstance(ret_type, str) + self._arg_type_name = arg_type + self.arg_type = None + self._ret_type_name = ret_type + self.ret_type = None + self.gen = gen + self.success_response = success_response + + def check(self, schema): + if self._arg_type_name: + self.arg_type = schema.lookup_type(self._arg_type_name) + assert isinstance(self.arg_type, QAPISchemaObjectType) + assert not self.arg_type.variants # not implemented + if self._ret_type_name: + self.ret_type = schema.lookup_type(self._ret_type_name) + assert isinstance(self.ret_type, QAPISchemaType) + + def visit(self, visitor): + visitor.visit_command(self.name, self.info, + self.arg_type, self.ret_type, + self.gen, self.success_response) + + +class QAPISchemaEvent(QAPISchemaEntity): + def __init__(self, name, info, arg_type): + QAPISchemaEntity.__init__(self, name, info) + assert not arg_type or isinstance(arg_type, str) + self._arg_type_name = arg_type + self.arg_type = None + + def check(self, schema): + if self._arg_type_name: + self.arg_type = schema.lookup_type(self._arg_type_name) + assert isinstance(self.arg_type, QAPISchemaObjectType) + assert not self.arg_type.variants # not implemented + + def visit(self, visitor): + visitor.visit_event(self.name, self.info, self.arg_type) + + +class QAPISchema(object): + def __init__(self, fname): + try: + self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs) + except (QAPISchemaError, QAPIExprError), err: + print >>sys.stderr, err + exit(1) + self._entity_dict = {} + self._def_predefineds() + self._def_exprs() + self.check() + + def _def_entity(self, ent): + assert ent.name not in self._entity_dict + self._entity_dict[ent.name] = ent + + def lookup_entity(self, name, typ=None): + ent = self._entity_dict.get(name) + if typ and not isinstance(ent, typ): + return None + return ent + + def lookup_type(self, name): + return self.lookup_entity(name, QAPISchemaType) + + def _def_builtin_type(self, name, json_type, c_type, c_null): + self._def_entity(QAPISchemaBuiltinType(name, json_type, + c_type, c_null)) + self._make_array_type(name) # TODO really needed? + + def _def_predefineds(self): + for t in [('str', 'string', 'char' + pointer_suffix, 'NULL'), + ('number', 'number', 'double', '0'), + ('int', 'int', 'int64_t', '0'), + ('int8', 'int', 'int8_t', '0'), + ('int16', 'int', 'int16_t', '0'), + ('int32', 'int', 'int32_t', '0'), + ('int64', 'int', 'int64_t', '0'), + ('uint8', 'int', 'uint8_t', '0'), + ('uint16', 'int', 'uint16_t', '0'), + ('uint32', 'int', 'uint32_t', '0'), + ('uint64', 'int', 'uint64_t', '0'), + ('size', 'int', 'uint64_t', '0'), + ('bool', 'boolean', 'bool', 'false'), + ('any', 'value', 'QObject' + pointer_suffix, 'NULL')]: + self._def_builtin_type(*t) + self.the_empty_object_type = QAPISchemaObjectType(':empty', None, None, + [], None) + self._def_entity(self.the_empty_object_type) + + def _make_implicit_enum_type(self, name, values): + name = name + 'Kind' + self._def_entity(QAPISchemaEnumType(name, None, values, None)) + return name + + def _make_array_type(self, element_type): + name = element_type + 'List' + if not self.lookup_type(name): + self._def_entity(QAPISchemaArrayType(name, None, element_type)) + return name + + def _make_implicit_object_type(self, name, role, members): + if not members: + return None + name = ':obj-%s-%s' % (name, role) + if not self.lookup_entity(name, QAPISchemaObjectType): + self._def_entity(QAPISchemaObjectType(name, None, None, + members, None)) + return name + + def _def_enum_type(self, expr, info): + name = expr['enum'] + data = expr['data'] + prefix = expr.get('prefix') + self._def_entity(QAPISchemaEnumType(name, info, data, prefix)) + self._make_array_type(name) # TODO really needed? + + def _make_member(self, name, typ): optional = False - if member.startswith('*'): - argname = member[1:] + if name.startswith('*'): + name = name[1:] optional = True - # Todo: allow argentry to be OrderedDict, for providing the - # value of an optional argument. - yield (argname, argentry, optional) + if isinstance(typ, list): + assert len(typ) == 1 + typ = self._make_array_type(typ[0]) + return QAPISchemaObjectTypeMember(name, typ, optional) + + def _make_members(self, data): + return [self._make_member(key, value) + for (key, value) in data.iteritems()] + + def _def_struct_type(self, expr, info): + name = expr['struct'] + base = expr.get('base') + data = expr['data'] + self._def_entity(QAPISchemaObjectType(name, info, base, + self._make_members(data), + None)) + self._make_array_type(name) # TODO really needed? + + def _make_variant(self, case, typ): + return QAPISchemaObjectTypeVariant(case, typ) + + def _make_simple_variant(self, case, typ): + if isinstance(typ, list): + assert len(typ) == 1 + typ = self._make_array_type(typ[0]) + typ = self._make_implicit_object_type(typ, 'wrapper', + [self._make_member('data', typ)]) + return QAPISchemaObjectTypeVariant(case, typ) + + def _make_tag_enum(self, type_name, variants): + return self._make_implicit_enum_type(type_name, + [v.name for v in variants]) + + def _def_union_type(self, expr, info): + name = expr['union'] + data = expr['data'] + base = expr.get('base') + tag_name = expr.get('discriminator') + tag_enum = None + if tag_name: + variants = [self._make_variant(key, value) + for (key, value) in data.iteritems()] + else: + variants = [self._make_simple_variant(key, value) + for (key, value) in data.iteritems()] + tag_enum = self._make_tag_enum(name, variants) + self._def_entity( + QAPISchemaObjectType(name, info, base, + self._make_members(OrderedDict()), + QAPISchemaObjectTypeVariants(tag_name, + tag_enum, + variants))) + self._make_array_type(name) # TODO really needed? + + def _def_alternate_type(self, expr, info): + name = expr['alternate'] + data = expr['data'] + variants = [self._make_variant(key, value) + for (key, value) in data.iteritems()] + tag_enum = self._make_tag_enum(name, variants) + self._def_entity( + QAPISchemaAlternateType(name, info, + QAPISchemaObjectTypeVariants(None, + tag_enum, + variants))) + self._make_array_type(name) # TODO really needed? + + def _def_command(self, expr, info): + name = expr['command'] + data = expr.get('data') + rets = expr.get('returns') + gen = expr.get('gen', True) + success_response = expr.get('success-response', True) + if isinstance(data, OrderedDict): + data = self._make_implicit_object_type(name, 'arg', + self._make_members(data)) + if isinstance(rets, list): + assert len(rets) == 1 + rets = self._make_array_type(rets[0]) + self._def_entity(QAPISchemaCommand(name, info, data, rets, gen, + success_response)) + + def _def_event(self, expr, info): + name = expr['event'] + data = expr.get('data') + if isinstance(data, OrderedDict): + data = self._make_implicit_object_type(name, 'arg', + self._make_members(data)) + self._def_entity(QAPISchemaEvent(name, info, data)) + + def _def_exprs(self): + for expr_elem in self.exprs: + expr = expr_elem['expr'] + info = expr_elem['info'] + if 'enum' in expr: + self._def_enum_type(expr, info) + elif 'struct' in expr: + self._def_struct_type(expr, info) + elif 'union' in expr: + self._def_union_type(expr, info) + elif 'alternate' in expr: + self._def_alternate_type(expr, info) + elif 'command' in expr: + self._def_command(expr, info) + elif 'event' in expr: + self._def_event(expr, info) + else: + assert False + + def check(self): + for ent in self._entity_dict.values(): + ent.check(self) + + def visit(self, visitor): + ignore = visitor.visit_begin(self) + for name in sorted(self._entity_dict.keys()): + if not ignore or not isinstance(self._entity_dict[name], ignore): + self._entity_dict[name].visit(visitor) + visitor.visit_end() + + +# +# Code generation helpers +# def camel_case(name): new_name = '' @@ -818,7 +1314,9 @@ def camel_to_upper(value): new_name += c return new_name.lstrip('_').upper() -def c_enum_const(type_name, const_name): +def c_enum_const(type_name, const_name, prefix=None): + if prefix is not None: + type_name = prefix return camel_to_upper(type_name + '_' + const_name) c_name_trans = string.maketrans('.-', '__') @@ -863,70 +1361,9 @@ def c_name(name, protect=True): return "q_" + name return name.translate(c_name_trans) -# Map type @name to the C typedef name for the list form. -# -# ['Name'] -> 'NameList', ['x-Foo'] -> 'x_FooList', ['int'] -> 'intList' -def c_list_type(name): - return type_name(name) + 'List' - -# Map type @value to the C typedef form. -# -# Used for converting 'type' from a 'member':'type' qapi definition -# into the alphanumeric portion of the type for a generated C parameter, -# as well as generated C function names. See c_type() for the rest of -# the conversion such as adding '*' on pointer types. -# 'int' -> 'int', '[x-Foo]' -> 'x_FooList', '__a.b_c' -> '__a_b_c' -def type_name(value): - if type(value) == list: - return c_list_type(value[0]) - if value in builtin_types.keys(): - return value - return c_name(value) - eatspace = '\033EATSPACE.' pointer_suffix = ' *' + eatspace -# Map type @name to its C type expression. -# If @is_param, const-qualify the string type. -# -# This function is used for computing the full C type of 'member':'name'. -# A special suffix is added in c_type() for pointer types, and it's -# stripped in mcgen(). So please notice this when you check the return -# value of c_type() outside mcgen(). -def c_type(value, is_param=False): - if value == 'str': - if is_param: - return 'const char' + pointer_suffix - return 'char' + pointer_suffix - - elif value == 'int': - return 'int64_t' - elif (value == 'int8' or value == 'int16' or value == 'int32' or - value == 'int64' or value == 'uint8' or value == 'uint16' or - value == 'uint32' or value == 'uint64'): - return value + '_t' - elif value == 'size': - return 'uint64_t' - elif value == 'bool': - return 'bool' - elif value == 'number': - return 'double' - elif type(value) == list: - return c_list_type(value[0]) + pointer_suffix - elif is_enum(value): - return c_name(value) - elif value == None: - return 'void' - elif value in events: - return camel_case(value) + 'Event' + pointer_suffix - else: - # complex type name - assert isinstance(value, str) and value != "" - return c_name(value) + pointer_suffix - -def is_c_ptr(value): - return c_type(value).endswith(pointer_suffix) - def genindent(count): ret = "" for i in range(count): @@ -943,24 +1380,26 @@ def pop_indent(indent_amount=4): global indent_level indent_level -= indent_amount +# Generate @code with @kwds interpolated. +# Obey indent_level, and strip eatspace. def cgen(code, **kwds): - indent = genindent(indent_level) - lines = code.split('\n') - lines = map(lambda x: indent + x, lines) - return '\n'.join(lines) % kwds + '\n' + raw = code % kwds + if indent_level: + indent = genindent(indent_level) + # re.subn() lacks flags support before Python 2.7, use re.compile() + raw = re.subn(re.compile("^.", re.MULTILINE), + indent + r'\g<0>', raw) + raw = raw[0] + return re.sub(re.escape(eatspace) + ' *', '', raw) def mcgen(code, **kwds): - raw = cgen('\n'.join(code.split('\n')[1:-1]), **kwds) - return re.sub(re.escape(eatspace) + ' *', '', raw) + if code[0] == '\n': + code = code[1:] + return cgen(code, **kwds) -def basename(filename): - return filename.split("/")[-1] def guardname(filename): - guard = basename(filename).rsplit(".", 1)[0] - for substr in [".", " ", "-"]: - guard = guard.replace(substr, "_") - return guard.upper() + '_H' + return c_name(filename, protect=False).upper() def guardstart(name): return mcgen(''' @@ -979,6 +1418,74 @@ def guardend(name): ''', name=guardname(name)) +def gen_enum_lookup(name, values, prefix=None): + ret = mcgen(''' + +const char *const %(c_name)s_lookup[] = { +''', + c_name=c_name(name)) + for value in values: + index = c_enum_const(name, value, prefix) + ret += mcgen(''' + [%(index)s] = "%(value)s", +''', + index=index, value=value) + + max_index = c_enum_const(name, 'MAX', prefix) + ret += mcgen(''' + [%(max_index)s] = NULL, +}; +''', + max_index=max_index) + return ret + +def gen_enum(name, values, prefix=None): + # append automatically generated _MAX value + enum_values = values + ['MAX'] + + ret = mcgen(''' + +typedef enum %(c_name)s { +''', + c_name=c_name(name)) + + i = 0 + for value in enum_values: + ret += mcgen(''' + %(c_enum)s = %(i)d, +''', + c_enum=c_enum_const(name, value, prefix), + i=i) + i += 1 + + ret += mcgen(''' +} %(c_name)s; +''', + c_name=c_name(name)) + + ret += mcgen(''' + +extern const char *const %(c_name)s_lookup[]; +''', + c_name=c_name(name)) + return ret + +def gen_params(arg_type, extra): + if not arg_type: + return extra + assert not arg_type.variants + ret = '' + sep = '' + for memb in arg_type.members: + ret += sep + sep = ', ' + if memb.optional: + ret += 'bool has_%s, ' % c_name(memb.name) + ret += '%s %s' % (memb.type.c_type(is_param=True), c_name(memb.name)) + if extra: + ret += sep + extra + return ret + # # Common command line parsing # @@ -1003,6 +1510,12 @@ def parse_command_line(extra_options = "", extra_long_options = []): for oa in opts: o, a = oa if o in ("-p", "--prefix"): + match = re.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a) + if match.end() != len(a): + print >>sys.stderr, \ + "%s: 'funny character '%s' in argument of --prefix" \ + % (sys.argv[0], a[match.end()]) + sys.exit(1) prefix = a elif o in ("-o", "--output-dir"): output_dir = a + "/" @@ -1030,14 +1543,16 @@ def parse_command_line(extra_options = "", extra_long_options = []): def open_output(output_dir, do_c, do_h, prefix, c_file, h_file, c_comment, h_comment): + guard = guardname(prefix + h_file) c_file = output_dir + prefix + c_file h_file = output_dir + prefix + h_file - try: - os.makedirs(output_dir) - except os.error, e: - if e.errno != errno.EEXIST: - raise + if output_dir: + try: + os.makedirs(output_dir) + except os.error, e: + if e.errno != errno.EEXIST: + raise def maybe_open(really, name, opt): if really: @@ -1062,7 +1577,7 @@ def open_output(output_dir, do_c, do_h, prefix, c_file, h_file, #define %(guard)s ''', - comment = h_comment, guard = guardname(h_file))) + comment = h_comment, guard = guard)) return (fdef, fdecl) diff --git a/scripts/qemu-gdb.py b/scripts/qemu-gdb.py index 6c7f4fbe53..d6f2e5a903 100644 --- a/scripts/qemu-gdb.py +++ b/scripts/qemu-gdb.py @@ -13,73 +13,20 @@ # Contributions after 2012-01-13 are licensed under the terms of the # GNU GPL, version 2 or (at your option) any later version. +# Usage: +# At the (gdb) prompt, type "source scripts/qemu-gdb.py". +# "help qemu" should then list the supported QEMU debug support commands. import gdb -def isnull(ptr): - return ptr == gdb.Value(0).cast(ptr.type) +import os, sys -def int128(p): - return long(p['lo']) + (long(p['hi']) << 64) +# Annoyingly, gdb doesn't put the directory of scripts onto the +# module search path. Do it manually. -def get_fs_base(): - '''Fetch %fs base value using arch_prctl(ARCH_GET_FS)''' - # %rsp - 120 is scratch space according to the SystemV ABI - old = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') - gdb.execute('call arch_prctl(0x1003, $rsp - 120)', False, True) - fs_base = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') - gdb.execute('set *(uint64_t*)($rsp - 120) = %s' % old, False, True) - return fs_base +sys.path.append(os.path.dirname(__file__)) -def get_glibc_pointer_guard(): - '''Fetch glibc pointer guard value''' - fs_base = get_fs_base() - return gdb.parse_and_eval('*(uint64_t*)((uint64_t)%s + 0x30)' % fs_base) - -def glibc_ptr_demangle(val, pointer_guard): - '''Undo effect of glibc's PTR_MANGLE()''' - return gdb.parse_and_eval('(((uint64_t)%s >> 0x11) | ((uint64_t)%s << (64 - 0x11))) ^ (uint64_t)%s' % (val, val, pointer_guard)) - -def bt_jmpbuf(jmpbuf): - '''Backtrace a jmpbuf''' - JB_RBX = 0 - JB_RBP = 1 - JB_R12 = 2 - JB_R13 = 3 - JB_R14 = 4 - JB_R15 = 5 - JB_RSP = 6 - JB_PC = 7 - - old_rbx = gdb.parse_and_eval('(uint64_t)$rbx') - old_rbp = gdb.parse_and_eval('(uint64_t)$rbp') - old_rsp = gdb.parse_and_eval('(uint64_t)$rsp') - old_r12 = gdb.parse_and_eval('(uint64_t)$r12') - old_r13 = gdb.parse_and_eval('(uint64_t)$r13') - old_r14 = gdb.parse_and_eval('(uint64_t)$r14') - old_r15 = gdb.parse_and_eval('(uint64_t)$r15') - old_rip = gdb.parse_and_eval('(uint64_t)$rip') - - pointer_guard = get_glibc_pointer_guard() - gdb.execute('set $rbx = %s' % jmpbuf[JB_RBX]) - gdb.execute('set $rbp = %s' % glibc_ptr_demangle(jmpbuf[JB_RBP], pointer_guard)) - gdb.execute('set $rsp = %s' % glibc_ptr_demangle(jmpbuf[JB_RSP], pointer_guard)) - gdb.execute('set $r12 = %s' % jmpbuf[JB_R12]) - gdb.execute('set $r13 = %s' % jmpbuf[JB_R13]) - gdb.execute('set $r14 = %s' % jmpbuf[JB_R14]) - gdb.execute('set $r15 = %s' % jmpbuf[JB_R15]) - gdb.execute('set $rip = %s' % glibc_ptr_demangle(jmpbuf[JB_PC], pointer_guard)) - - gdb.execute('bt') - - gdb.execute('set $rbx = %s' % old_rbx) - gdb.execute('set $rbp = %s' % old_rbp) - gdb.execute('set $rsp = %s' % old_rsp) - gdb.execute('set $r12 = %s' % old_r12) - gdb.execute('set $r13 = %s' % old_r13) - gdb.execute('set $r14 = %s' % old_r14) - gdb.execute('set $r15 = %s' % old_r15) - gdb.execute('set $rip = %s' % old_rip) +from qemugdb import mtree, coroutine class QemuCommand(gdb.Command): '''Prefix for QEMU debug support commands''' @@ -87,78 +34,10 @@ class QemuCommand(gdb.Command): gdb.Command.__init__(self, 'qemu', gdb.COMMAND_DATA, gdb.COMPLETE_NONE, True) -class CoroutineCommand(gdb.Command): - '''Display coroutine backtrace''' - def __init__(self): - gdb.Command.__init__(self, 'qemu coroutine', gdb.COMMAND_DATA, - gdb.COMPLETE_NONE) - - def invoke(self, arg, from_tty): - argv = gdb.string_to_argv(arg) - if len(argv) != 1: - gdb.write('usage: qemu coroutine <coroutine-pointer>\n') - return - - coroutine_pointer = gdb.parse_and_eval(argv[0]).cast(gdb.lookup_type('CoroutineUContext').pointer()) - bt_jmpbuf(coroutine_pointer['env']['__jmpbuf']) - -class MtreeCommand(gdb.Command): - '''Display the memory tree hierarchy''' - def __init__(self): - gdb.Command.__init__(self, 'qemu mtree', gdb.COMMAND_DATA, - gdb.COMPLETE_NONE) - self.queue = [] - def invoke(self, arg, from_tty): - self.seen = set() - self.queue_root('address_space_memory') - self.queue_root('address_space_io') - self.process_queue() - def queue_root(self, varname): - ptr = gdb.parse_and_eval(varname)['root'] - self.queue.append(ptr) - def process_queue(self): - while self.queue: - ptr = self.queue.pop(0) - if long(ptr) in self.seen: - continue - self.print_item(ptr) - def print_item(self, ptr, offset = gdb.Value(0), level = 0): - self.seen.add(long(ptr)) - addr = ptr['addr'] - addr += offset - size = int128(ptr['size']) - alias = ptr['alias'] - klass = '' - if not isnull(alias): - klass = ' (alias)' - elif not isnull(ptr['ops']): - klass = ' (I/O)' - elif bool(ptr['ram']): - klass = ' (RAM)' - gdb.write('%s%016x-%016x %s%s (@ %s)\n' - % (' ' * level, - long(addr), - long(addr + (size - 1)), - ptr['name'].string(), - klass, - ptr, - ), - gdb.STDOUT) - if not isnull(alias): - gdb.write('%s alias: %s@%016x (@ %s)\n' % - (' ' * level, - alias['name'].string(), - ptr['alias_offset'], - alias, - ), - gdb.STDOUT) - self.queue.append(alias) - subregion = ptr['subregions']['tqh_first'] - level += 1 - while not isnull(subregion): - self.print_item(subregion, addr, level) - subregion = subregion['subregions_link']['tqe_next'] - QemuCommand() -CoroutineCommand() -MtreeCommand() +coroutine.CoroutineCommand() +mtree.MtreeCommand() + +# Default to silently passing through SIGUSR1, because QEMU sends it +# to itself a lot. +gdb.execute('handle SIGUSR1 pass noprint nostop') diff --git a/scripts/qemugdb/__init__.py b/scripts/qemugdb/__init__.py new file mode 100644 index 0000000000..969f552b26 --- /dev/null +++ b/scripts/qemugdb/__init__.py @@ -0,0 +1,28 @@ +#!/usr/bin/python + +# GDB debugging support +# +# Copyright (c) 2015 Linaro Ltd +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see +# <http://www.gnu.org/licenses/gpl-2.0.html> +# + +# We don't need to do anything in our init file currently. + +""" +Support routines for debugging QEMU under GDB +""" + +__license__ = "GPL version 2 or (at your option) any later version" diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py new file mode 100644 index 0000000000..3c54918b5d --- /dev/null +++ b/scripts/qemugdb/coroutine.py @@ -0,0 +1,91 @@ +#!/usr/bin/python + +# GDB debugging support +# +# Copyright 2012 Red Hat, Inc. and/or its affiliates +# +# Authors: +# Avi Kivity <avi@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# +# Contributions after 2012-01-13 are licensed under the terms of the +# GNU GPL, version 2 or (at your option) any later version. + +import gdb + +def get_fs_base(): + '''Fetch %fs base value using arch_prctl(ARCH_GET_FS)''' + # %rsp - 120 is scratch space according to the SystemV ABI + old = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') + gdb.execute('call arch_prctl(0x1003, $rsp - 120)', False, True) + fs_base = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') + gdb.execute('set *(uint64_t*)($rsp - 120) = %s' % old, False, True) + return fs_base + +def get_glibc_pointer_guard(): + '''Fetch glibc pointer guard value''' + fs_base = get_fs_base() + return gdb.parse_and_eval('*(uint64_t*)((uint64_t)%s + 0x30)' % fs_base) + +def glibc_ptr_demangle(val, pointer_guard): + '''Undo effect of glibc's PTR_MANGLE()''' + return gdb.parse_and_eval('(((uint64_t)%s >> 0x11) | ((uint64_t)%s << (64 - 0x11))) ^ (uint64_t)%s' % (val, val, pointer_guard)) + +def bt_jmpbuf(jmpbuf): + '''Backtrace a jmpbuf''' + JB_RBX = 0 + JB_RBP = 1 + JB_R12 = 2 + JB_R13 = 3 + JB_R14 = 4 + JB_R15 = 5 + JB_RSP = 6 + JB_PC = 7 + + old_rbx = gdb.parse_and_eval('(uint64_t)$rbx') + old_rbp = gdb.parse_and_eval('(uint64_t)$rbp') + old_rsp = gdb.parse_and_eval('(uint64_t)$rsp') + old_r12 = gdb.parse_and_eval('(uint64_t)$r12') + old_r13 = gdb.parse_and_eval('(uint64_t)$r13') + old_r14 = gdb.parse_and_eval('(uint64_t)$r14') + old_r15 = gdb.parse_and_eval('(uint64_t)$r15') + old_rip = gdb.parse_and_eval('(uint64_t)$rip') + + pointer_guard = get_glibc_pointer_guard() + gdb.execute('set $rbx = %s' % jmpbuf[JB_RBX]) + gdb.execute('set $rbp = %s' % glibc_ptr_demangle(jmpbuf[JB_RBP], pointer_guard)) + gdb.execute('set $rsp = %s' % glibc_ptr_demangle(jmpbuf[JB_RSP], pointer_guard)) + gdb.execute('set $r12 = %s' % jmpbuf[JB_R12]) + gdb.execute('set $r13 = %s' % jmpbuf[JB_R13]) + gdb.execute('set $r14 = %s' % jmpbuf[JB_R14]) + gdb.execute('set $r15 = %s' % jmpbuf[JB_R15]) + gdb.execute('set $rip = %s' % glibc_ptr_demangle(jmpbuf[JB_PC], pointer_guard)) + + gdb.execute('bt') + + gdb.execute('set $rbx = %s' % old_rbx) + gdb.execute('set $rbp = %s' % old_rbp) + gdb.execute('set $rsp = %s' % old_rsp) + gdb.execute('set $r12 = %s' % old_r12) + gdb.execute('set $r13 = %s' % old_r13) + gdb.execute('set $r14 = %s' % old_r14) + gdb.execute('set $r15 = %s' % old_r15) + gdb.execute('set $rip = %s' % old_rip) + + +class CoroutineCommand(gdb.Command): + '''Display coroutine backtrace''' + def __init__(self): + gdb.Command.__init__(self, 'qemu coroutine', gdb.COMMAND_DATA, + gdb.COMPLETE_NONE) + + def invoke(self, arg, from_tty): + argv = gdb.string_to_argv(arg) + if len(argv) != 1: + gdb.write('usage: qemu coroutine <coroutine-pointer>\n') + return + + coroutine_pointer = gdb.parse_and_eval(argv[0]).cast(gdb.lookup_type('CoroutineUContext').pointer()) + bt_jmpbuf(coroutine_pointer['env']['__jmpbuf']) diff --git a/scripts/qemugdb/mtree.py b/scripts/qemugdb/mtree.py new file mode 100644 index 0000000000..06011c30cc --- /dev/null +++ b/scripts/qemugdb/mtree.py @@ -0,0 +1,82 @@ +#!/usr/bin/python + +# GDB debugging support +# +# Copyright 2012 Red Hat, Inc. and/or its affiliates +# +# Authors: +# Avi Kivity <avi@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# +# Contributions after 2012-01-13 are licensed under the terms of the +# GNU GPL, version 2 or (at your option) any later version. + +# 'qemu mtree' -- display the memory hierarchy + +import gdb + +def isnull(ptr): + return ptr == gdb.Value(0).cast(ptr.type) + +def int128(p): + return long(p['lo']) + (long(p['hi']) << 64) + +class MtreeCommand(gdb.Command): + '''Display the memory tree hierarchy''' + def __init__(self): + gdb.Command.__init__(self, 'qemu mtree', gdb.COMMAND_DATA, + gdb.COMPLETE_NONE) + self.queue = [] + def invoke(self, arg, from_tty): + self.seen = set() + self.queue_root('address_space_memory') + self.queue_root('address_space_io') + self.process_queue() + def queue_root(self, varname): + ptr = gdb.parse_and_eval(varname)['root'] + self.queue.append(ptr) + def process_queue(self): + while self.queue: + ptr = self.queue.pop(0) + if long(ptr) in self.seen: + continue + self.print_item(ptr) + def print_item(self, ptr, offset = gdb.Value(0), level = 0): + self.seen.add(long(ptr)) + addr = ptr['addr'] + addr += offset + size = int128(ptr['size']) + alias = ptr['alias'] + klass = '' + if not isnull(alias): + klass = ' (alias)' + elif not isnull(ptr['ops']): + klass = ' (I/O)' + elif bool(ptr['ram']): + klass = ' (RAM)' + gdb.write('%s%016x-%016x %s%s (@ %s)\n' + % (' ' * level, + long(addr), + long(addr + (size - 1)), + ptr['name'].string(), + klass, + ptr, + ), + gdb.STDOUT) + if not isnull(alias): + gdb.write('%s alias: %s@%016x (@ %s)\n' % + (' ' * level, + alias['name'].string(), + ptr['alias_offset'], + alias, + ), + gdb.STDOUT) + self.queue.append(alias) + subregion = ptr['subregions']['tqh_first'] + level += 1 + while not isnull(subregion): + self.print_item(subregion, addr, level) + subregion = subregion['subregions_link']['tqe_next'] + diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index 65280d29d1..fa39bf0d7b 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -29,6 +29,41 @@ # (QEMU) device_add driver=e1000 id=net1 # {u'return': {}} # (QEMU) +# +# key=value pairs also support Python or JSON object literal subset notations, +# without spaces. Dictionaries/objects {} are supported as are arrays []. +# +# example-command arg-name1={'key':'value','obj'={'prop':"value"}} +# +# Both JSON and Python formatting should work, including both styles of +# string literal quotes. Both paradigms of literal values should work, +# including null/true/false for JSON and None/True/False for Python. +# +# +# Transactions have the following multi-line format: +# +# transaction( +# action-name1 [ arg-name1=arg1 ] ... [arg-nameN=argN ] +# ... +# action-nameN [ arg-name1=arg1 ] ... [arg-nameN=argN ] +# ) +# +# One line transactions are also supported: +# +# transaction( action-name1 ... ) +# +# For example: +# +# (QEMU) transaction( +# TRANS> block-dirty-bitmap-add node=drive0 name=bitmap1 +# TRANS> block-dirty-bitmap-clear node=drive0 name=bitmap0 +# TRANS> ) +# {"return": {}} +# (QEMU) +# +# Use the -v and -p options to activate the verbose and pretty-print options, +# which will echo back the properly formatted JSON-compliant QMP that is being +# sent to QEMU, which is useful for debugging and documentation generation. import qmp import json diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index f0e830c2d6..1107619121 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -28,38 +28,32 @@ if [ -z "$output" ]; then output="$PWD" fi -cp_virtio() { - from=$1 +cp_portable() { + f=$1 to=$2 - virtio=$(find "$from" -name '*virtio*h' -o -name "input.h" -o -name "pci_regs.h") - if [ "$virtio" ]; then - rm -rf "$to" - mkdir -p "$to" - for f in $virtio; do - if - grep '#include' "$f" | grep -v -e 'linux/virtio' \ - -e 'linux/types' \ - -e 'linux/if_ether' \ - -e 'sys/' \ - > /dev/null - then - echo "Unexpected #include in input file $f". - exit 2 - fi - - header=$(basename "$f"); - sed -e 's/__u\([0-9][0-9]*\)/uint\1_t/g' \ - -e 's/__s\([0-9][0-9]*\)/int\1_t/g' \ - -e 's/__le\([0-9][0-9]*\)/uint\1_t/g' \ - -e 's/__be\([0-9][0-9]*\)/uint\1_t/g' \ - -e 's/<linux\/\([^>]*\)>/"standard-headers\/linux\/\1"/' \ - -e 's/__bitwise__//' \ - -e 's/__attribute__((packed))/QEMU_PACKED/' \ - -e 's/__inline__/inline/' \ - -e '/sys\/ioctl.h/d' \ - "$f" > "$to/$header"; - done + if + grep '#include' "$f" | grep -v -e 'linux/virtio' \ + -e 'linux/types' \ + -e 'stdint' \ + -e 'linux/if_ether' \ + -e 'sys/' \ + > /dev/null + then + echo "Unexpected #include in input file $f". + exit 2 fi + + header=$(basename "$f"); + sed -e 's/__u\([0-9][0-9]*\)/uint\1_t/g' \ + -e 's/__s\([0-9][0-9]*\)/int\1_t/g' \ + -e 's/__le\([0-9][0-9]*\)/uint\1_t/g' \ + -e 's/__be\([0-9][0-9]*\)/uint\1_t/g' \ + -e 's/<linux\/\([^>]*\)>/"standard-headers\/linux\/\1"/' \ + -e 's/__bitwise__//' \ + -e 's/__attribute__((packed))/QEMU_PACKED/' \ + -e 's/__inline__/inline/' \ + -e '/sys\/ioctl.h/d' \ + "$f" > "$to/$header"; } # This will pick up non-directories too (eg "Kconfig") but we will @@ -85,14 +79,19 @@ for arch in $ARCHLIST; do for header in kvm.h kvm_para.h; do cp "$tmpdir/include/asm/$header" "$output/linux-headers/asm-$arch" done - if [ $arch = x86 ]; then - cp "$tmpdir/include/asm/hyperv.h" "$output/linux-headers/asm-x86" - fi if [ $arch = powerpc ]; then cp "$tmpdir/include/asm/epapr_hcalls.h" "$output/linux-headers/asm-powerpc/" fi - cp_virtio "$tmpdir/include/asm" "$output/include/standard-headers/asm-$arch" + rm -rf "$output/include/standard-headers/asm-$arch" + mkdir -p "$output/include/standard-headers/asm-$arch" + if [ $arch = s390 ]; then + cp_portable "$tmpdir/include/asm/kvm_virtio.h" "$output/include/standard-headers/asm-s390/" + cp_portable "$tmpdir/include/asm/virtio-ccw.h" "$output/include/standard-headers/asm-s390/" + fi + if [ $arch = x86 ]; then + cp_portable "$tmpdir/include/asm/hyperv.h" "$output/include/standard-headers/asm-x86/" + fi done rm -rf "$output/linux-headers/linux" @@ -112,6 +111,9 @@ else cp "$linux/COPYING" "$output/linux-headers" fi +cat <<EOF >$output/linux-headers/asm-x86/hyperv.h +#include "standard-headers/asm-x86/hyperv.h" +EOF cat <<EOF >$output/linux-headers/linux/virtio_config.h #include "standard-headers/linux/virtio_config.h" EOF @@ -119,7 +121,12 @@ cat <<EOF >$output/linux-headers/linux/virtio_ring.h #include "standard-headers/linux/virtio_ring.h" EOF -cp_virtio "$tmpdir/include/linux/" "$output/include/standard-headers/linux" +rm -rf "$output/include/standard-headers/linux" +mkdir -p "$output/include/standard-headers/linux" +for i in "$tmpdir"/include/linux/*virtio*.h "$tmpdir/include/linux/input.h" \ + "$tmpdir/include/linux/pci_regs.h"; do + cp_portable "$i" "$output/include/standard-headers/linux" +done cat <<EOF >$output/include/standard-headers/linux/types.h #include <stdint.h> |