linux-yocto/scripts/check-sysctl-docs
Stephen Kitt 021622df55 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>
2020-02-25 03:35:16 -07:00

4.4 KiB
Executable File

#!/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 } } }