docs: add a script to check sysctl docs

This script allows sysctl documentation to be checked against the
kernel source code, to identify missing or obsolete entries. Running
it against 5.5 shows for example that sysctl/kernel.rst has two
obsolete entries and is missing 52 entries.

Signed-off-by: Stephen Kitt <steve@sk2.org>
Signed-off-by: Jonathan Corbet <corbet@lwn.net>
This commit is contained in:
Stephen Kitt 2020-02-19 16:34:42 +01:00 committed by Jonathan Corbet
parent bf347b9da9
commit 021622df55
2 changed files with 184 additions and 0 deletions

View File

@ -2,6 +2,9 @@
Documentation for /proc/sys/kernel/ Documentation for /proc/sys/kernel/
=================================== ===================================
.. See scripts/check-sysctl-docs to keep this up to date
Copyright (c) 1998, 1999, Rik van Riel <riel@nl.linux.org> Copyright (c) 1998, 1999, Rik van Riel <riel@nl.linux.org>
Copyright (c) 2009, Shen Feng<shen@cn.fujitsu.com> Copyright (c) 2009, Shen Feng<shen@cn.fujitsu.com>

181
scripts/check-sysctl-docs Executable file
View File

@ -0,0 +1,181 @@
#!/usr/bin/gawk -f
# SPDX-License-Identifier: GPL-2.0
# Script to check sysctl documentation against source files
#
# Copyright (c) 2020 Stephen Kitt
# Example invocation:
# scripts/check-sysctl-docs -vtable="kernel" \
# Documentation/admin-guide/sysctl/kernel.rst \
# $(git grep -l register_sysctl_)
#
# Specify -vdebug=1 to see debugging information
BEGIN {
if (!table) {
print "Please specify the table to look for using the table variable" > "/dev/stderr"
exit 1
}
}
# The following globals are used:
# children: maps ctl_table names and procnames to child ctl_table names
# documented: maps documented entries (each key is an entry)
# entries: maps ctl_table names and procnames to counts (so
# enumerating the subkeys for a given ctl_table lists its
# procnames)
# files: maps procnames to source file names
# paths: maps ctl_path names to paths
# curpath: the name of the current ctl_path struct
# curtable: the name of the current ctl_table struct
# curentry: the name of the current proc entry (procname when parsing
# a ctl_table, constructed path when parsing a ctl_path)
# Remove punctuation from the given value
function trimpunct(value) {
while (value ~ /^["&]/) {
value = substr(value, 2)
}
while (value ~ /[]["&,}]$/) {
value = substr(value, 1, length(value) - 1)
}
return value
}
# Print the information for the given entry
function printentry(entry) {
seen[entry]++
printf "* %s from %s", entry, file[entry]
if (documented[entry]) {
printf " (documented)"
}
print ""
}
# Stage 1: build the list of documented entries
FNR == NR && /^=+$/ {
if (prevline ~ /Documentation for/) {
# This is the main title
next
}
# The previous line is a section title, parse it
$0 = prevline
if (debug) print "Parsing " $0
inbrackets = 0
for (i = 1; i <= NF; i++) {
if (length($i) == 0) {
continue
}
if (!inbrackets && substr($i, 1, 1) == "(") {
inbrackets = 1
}
if (!inbrackets) {
token = trimpunct($i)
if (length(token) > 0 && token != "and") {
if (debug) print trimpunct($i)
documented[trimpunct($i)]++
}
}
if (inbrackets && substr($i, length($i), 1) == ")") {
inbrackets = 0
}
}
}
FNR == NR {
prevline = $0
next
}
# Stage 2: process each file and find all sysctl tables
BEGINFILE {
delete children
delete entries
delete paths
curpath = ""
curtable = ""
curentry = ""
if (debug) print "Processing file " FILENAME
}
/^static struct ctl_path/ {
match($0, /static struct ctl_path ([^][]+)/, tables)
curpath = tables[1]
if (debug) print "Processing path " curpath
}
/^static struct ctl_table/ {
match($0, /static struct ctl_table ([^][]+)/, tables)
curtable = tables[1]
if (debug) print "Processing table " curtable
}
/^};$/ {
curpath = ""
curtable = ""
curentry = ""
}
curpath && /\.procname[\t ]*=[\t ]*".+"/ {
match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
if (curentry) {
curentry = curentry "/" names[1]
} else {
curentry = names[1]
}
if (debug) print "Setting path " curpath " to " curentry
paths[curpath] = curentry
}
curtable && /\.procname[\t ]*=[\t ]*".+"/ {
match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
curentry = names[1]
if (debug) print "Adding entry " curentry " to table " curtable
entries[curtable][curentry]++
file[curentry] = FILENAME
}
/\.child[\t ]*=/ {
child = trimpunct($NF)
if (debug) print "Linking child " child " to table " curtable " entry " curentry
children[curtable][curentry] = child
}
/register_sysctl_table\(.*\)/ {
match($0, /register_sysctl_table\(([^)]+)\)/, tables)
if (debug) print "Registering table " tables[1]
if (children[tables[1]][table]) {
for (entry in entries[children[tables[1]][table]]) {
printentry(entry)
}
}
}
/register_sysctl_paths\(.*\)/ {
match($0, /register_sysctl_paths\(([^)]+), ([^)]+)\)/, tables)
if (debug) print "Attaching table " tables[2] " to path " tables[1]
if (paths[tables[1]] == table) {
for (entry in entries[tables[2]]) {
printentry(entry)
}
}
split(paths[tables[1]], components, "/")
if (length(components) > 1 && components[1] == table) {
# Count the first subdirectory as seen
seen[components[2]]++
}
}
END {
for (entry in documented) {
if (!seen[entry]) {
print "No implementation for " entry
}
}
}