check-sysctl-docs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. #!/usr/bin/gawk -f
  2. # SPDX-License-Identifier: GPL-2.0
  3. # Script to check sysctl documentation against source files
  4. #
  5. # Copyright (c) 2020 Stephen Kitt
  6. # Example invocation:
  7. # scripts/check-sysctl-docs -vtable="kernel" \
  8. # Documentation/admin-guide/sysctl/kernel.rst \
  9. # $(git grep -l register_sysctl)
  10. #
  11. # Specify -vdebug=1 to see debugging information
  12. BEGIN {
  13. if (!table) {
  14. print "Please specify the table to look for using the table variable" > "/dev/stderr"
  15. exit 1
  16. }
  17. }
  18. # The following globals are used:
  19. # documented: maps documented entries (each key is an entry)
  20. # entries: maps ctl_table names and procnames to counts (so
  21. # enumerating the subkeys for a given ctl_table lists its
  22. # procnames)
  23. # curtable: the name of the current ctl_table struct
  24. # curentry: the name of the current proc entry (procname when parsing
  25. # a ctl_table, constructed path when parsing a ctl_path)
  26. # Remove punctuation from the given value
  27. function trimpunct(value) {
  28. while (value ~ /^["&]/) {
  29. value = substr(value, 2)
  30. }
  31. while (value ~ /[]["&,}]$/) {
  32. value = substr(value, 1, length(value) - 1)
  33. }
  34. return value
  35. }
  36. # Print the information for the given entry
  37. function printentry(entry) {
  38. seen[entry]++
  39. printf "* %s from %s", entry, file[entry]
  40. if (documented[entry]) {
  41. printf " (documented)"
  42. }
  43. print ""
  44. }
  45. # Stage 1: build the list of documented entries
  46. FNR == NR && /^=+$/ {
  47. if (prevline ~ /Documentation for/) {
  48. # This is the main title
  49. next
  50. }
  51. # The previous line is a section title, parse it
  52. $0 = prevline
  53. if (debug) print "Parsing " $0
  54. inbrackets = 0
  55. for (i = 1; i <= NF; i++) {
  56. if (length($i) == 0) {
  57. continue
  58. }
  59. if (!inbrackets && substr($i, 1, 1) == "(") {
  60. inbrackets = 1
  61. }
  62. if (!inbrackets) {
  63. token = trimpunct($i)
  64. if (length(token) > 0 && token != "and") {
  65. if (debug) print trimpunct($i)
  66. documented[trimpunct($i)]++
  67. }
  68. }
  69. if (inbrackets && substr($i, length($i), 1) == ")") {
  70. inbrackets = 0
  71. }
  72. }
  73. }
  74. FNR == NR {
  75. prevline = $0
  76. next
  77. }
  78. # Stage 2: process each file and find all sysctl tables
  79. BEGINFILE {
  80. delete entries
  81. curtable = ""
  82. curentry = ""
  83. delete vars
  84. if (debug) print "Processing file " FILENAME
  85. }
  86. /^static( const)? struct ctl_table/ {
  87. match($0, /static( const)? struct ctl_table ([^][]+)/, tables)
  88. curtable = tables[2]
  89. if (debug) print "Processing table " curtable
  90. }
  91. /^};$/ {
  92. curtable = ""
  93. curentry = ""
  94. delete vars
  95. }
  96. curtable && /\.procname[\t ]*=[\t ]*".+"/ {
  97. match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
  98. curentry = names[1]
  99. if (debug) print "Adding entry " curentry " to table " curtable
  100. entries[curtable][curentry]++
  101. file[curentry] = FILENAME
  102. }
  103. /register_sysctl.*/ {
  104. match($0, /register_sysctl(|_init|_sz)\("([^"]+)" *, *([^,)]+)/, tables)
  105. if (debug) print "Registering table " tables[3] " at " tables[2]
  106. if (tables[2] == table) {
  107. for (entry in entries[tables[3]]) {
  108. printentry(entry)
  109. }
  110. }
  111. }
  112. /kmemdup.*/ {
  113. match($0, /([^ \t]+) *= *kmemdup\(([^,]+) *,/, names)
  114. if (debug) print "Found variable " names[1] " for table " names[2]
  115. if (names[2] in entries) {
  116. vars[names[1]] = names[2]
  117. }
  118. }
  119. /__register_sysctl_table.*/ {
  120. match($0, /__register_sysctl_table\([^,]+, *"([^"]+)" *, *([^,]+)/, tables)
  121. if (debug) print "Registering variable table " tables[2] " at " tables[1]
  122. if (tables[1] == table && tables[2] in vars) {
  123. for (entry in entries[vars[tables[2]]]) {
  124. printentry(entry)
  125. }
  126. }
  127. }
  128. END {
  129. for (entry in documented) {
  130. if (!seen[entry]) {
  131. print "No implementation for " entry
  132. }
  133. }
  134. }