check-sysctl-docs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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. # children: maps ctl_table names and procnames to child ctl_table names
  20. # documented: maps documented entries (each key is an entry)
  21. # entries: maps ctl_table names and procnames to counts (so
  22. # enumerating the subkeys for a given ctl_table lists its
  23. # procnames)
  24. # files: maps procnames to source file names
  25. # paths: maps ctl_path names to paths
  26. # curpath: the name of the current ctl_path struct
  27. # curtable: the name of the current ctl_table struct
  28. # curentry: the name of the current proc entry (procname when parsing
  29. # a ctl_table, constructed path when parsing a ctl_path)
  30. # Remove punctuation from the given value
  31. function trimpunct(value) {
  32. while (value ~ /^["&]/) {
  33. value = substr(value, 2)
  34. }
  35. while (value ~ /[]["&,}]$/) {
  36. value = substr(value, 1, length(value) - 1)
  37. }
  38. return value
  39. }
  40. # Print the information for the given entry
  41. function printentry(entry) {
  42. seen[entry]++
  43. printf "* %s from %s", entry, file[entry]
  44. if (documented[entry]) {
  45. printf " (documented)"
  46. }
  47. print ""
  48. }
  49. # Stage 1: build the list of documented entries
  50. FNR == NR && /^=+$/ {
  51. if (prevline ~ /Documentation for/) {
  52. # This is the main title
  53. next
  54. }
  55. # The previous line is a section title, parse it
  56. $0 = prevline
  57. if (debug) print "Parsing " $0
  58. inbrackets = 0
  59. for (i = 1; i <= NF; i++) {
  60. if (length($i) == 0) {
  61. continue
  62. }
  63. if (!inbrackets && substr($i, 1, 1) == "(") {
  64. inbrackets = 1
  65. }
  66. if (!inbrackets) {
  67. token = trimpunct($i)
  68. if (length(token) > 0 && token != "and") {
  69. if (debug) print trimpunct($i)
  70. documented[trimpunct($i)]++
  71. }
  72. }
  73. if (inbrackets && substr($i, length($i), 1) == ")") {
  74. inbrackets = 0
  75. }
  76. }
  77. }
  78. FNR == NR {
  79. prevline = $0
  80. next
  81. }
  82. # Stage 2: process each file and find all sysctl tables
  83. BEGINFILE {
  84. delete children
  85. delete entries
  86. delete paths
  87. curpath = ""
  88. curtable = ""
  89. curentry = ""
  90. if (debug) print "Processing file " FILENAME
  91. }
  92. /^static struct ctl_path/ {
  93. match($0, /static struct ctl_path ([^][]+)/, tables)
  94. curpath = tables[1]
  95. if (debug) print "Processing path " curpath
  96. }
  97. /^static struct ctl_table/ {
  98. match($0, /static struct ctl_table ([^][]+)/, tables)
  99. curtable = tables[1]
  100. if (debug) print "Processing table " curtable
  101. }
  102. /^};$/ {
  103. curpath = ""
  104. curtable = ""
  105. curentry = ""
  106. }
  107. curpath && /\.procname[\t ]*=[\t ]*".+"/ {
  108. match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
  109. if (curentry) {
  110. curentry = curentry "/" names[1]
  111. } else {
  112. curentry = names[1]
  113. }
  114. if (debug) print "Setting path " curpath " to " curentry
  115. paths[curpath] = curentry
  116. }
  117. curtable && /\.procname[\t ]*=[\t ]*".+"/ {
  118. match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
  119. curentry = names[1]
  120. if (debug) print "Adding entry " curentry " to table " curtable
  121. entries[curtable][curentry]++
  122. file[curentry] = FILENAME
  123. }
  124. /\.child[\t ]*=/ {
  125. child = trimpunct($NF)
  126. if (debug) print "Linking child " child " to table " curtable " entry " curentry
  127. children[curtable][curentry] = child
  128. }
  129. /register_sysctl_table\(.*\)/ {
  130. match($0, /register_sysctl_table\(([^)]+)\)/, tables)
  131. if (debug) print "Registering table " tables[1]
  132. if (children[tables[1]][table]) {
  133. for (entry in entries[children[tables[1]][table]]) {
  134. printentry(entry)
  135. }
  136. }
  137. }
  138. /register_sysctl_paths\(.*\)/ {
  139. match($0, /register_sysctl_paths\(([^)]+), ([^)]+)\)/, tables)
  140. if (debug) print "Attaching table " tables[2] " to path " tables[1]
  141. if (paths[tables[1]] == table) {
  142. for (entry in entries[tables[2]]) {
  143. printentry(entry)
  144. }
  145. }
  146. split(paths[tables[1]], components, "/")
  147. if (length(components) > 1 && components[1] == table) {
  148. # Count the first subdirectory as seen
  149. seen[components[2]]++
  150. }
  151. }
  152. END {
  153. for (entry in documented) {
  154. if (!seen[entry]) {
  155. print "No implementation for " entry
  156. }
  157. }
  158. }