freebsd-dev/usr.bin/kdump/mksubr
Rui Paulo 836450ab23 When generating functions to print the arguments of system calls with
bitwise parameters (e.g. mmap), print the syscall parameter value first.
The resulting output looks like the %b specifier of printf(9).

Before:
mmap(0,0x8000,PROT_READ|PROT_WRITE,...
After:
mmap(0,0x8000,0x3<PROT_READ|PROT_WRITE>,...

Submitted by:	Norberto Lopes <nlopes.ml at gmail.com>
Idea from:	freebsd-arch/2006-April/005116.html
2010-09-16 11:33:31 +00:00

451 lines
11 KiB
Bash

#!/bin/sh
#
# $FreeBSD$
#
# Generates kdump_subr.c
# mkioctls is a special-purpose script, and works fine as it is
# now, so it remains independent. The idea behind how it generates
# its list was heavily borrowed here.
#
# Some functions here are automatically generated. This can mean
# the user will see unusual kdump output or errors while building
# if the underlying .h files are changed significantly.
#
# Key:
# AUTO: Completely auto-generated with either the "or" or the "switch"
# method.
# AUTO - Special: Generated automatically, but with some extra commands
# that the auto_*_type() functions are inappropriate for.
# MANUAL: Manually entered and must therefore be manually updated.
set -e
LC_ALL=C; export LC_ALL
if [ -z "$1" ]
then
echo "usage: sh $0 include-dir"
exit 1
fi
include_dir=$1
#
# Automatically generates a C function that will print out the
# numeric input as a pipe-delimited string of the appropriate
# #define keys. ex:
# S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH
# The XOR is necessary to prevent including the "0"-value in every
# line.
#
auto_or_type () {
local name grep file
name=$1
grep=$2
file=$3
cat <<_EOF_
/* AUTO */
void
$name (int arg)
{
int or = 0;
printf("%#x<", arg);
_EOF_
egrep "^#[[:space:]]*define[[:space:]]+"${grep}"[[:space:]]*" \
$include_dir/$file | \
awk '{ for (i = 1; i <= NF; i++) \
if ($i ~ /define/) \
break; \
++i; \
printf "\tif(!((arg>0)^((%s)>0)))\n\t\tif_print_or(arg, %s, or);\n", $i, $i }'
cat <<_EOF_
printf(">");
if (or == 0)
(void)printf("<invalid>%ld", (long)arg);
}
_EOF_
}
#
# Automatically generates a C function used when the argument
# maps to a single, specific #definition
#
auto_switch_type () {
local name grep file
name=$1
grep=$2
file=$3
cat <<_EOF_
/* AUTO */
void
$name (int arg)
{
switch (arg) {
_EOF_
egrep "^#[[:space:]]*define[[:space:]]+"${grep}"[[:space:]]*" \
$include_dir/$file | \
awk '{ for (i = 1; i <= NF; i++) \
if ($i ~ /define/) \
break; \
++i; \
printf "\tcase %s:\n\t\t(void)printf(\"%s\");\n\t\tbreak;\n", $i, $i }'
cat <<_EOF_
default: /* Should not reach */
(void)printf("<invalid=%ld>", (long)arg);
}
}
_EOF_
}
#
# Automatically generates a C function used when the argument
# maps to a #definition
#
auto_if_type () {
local name grep file
name=$1
grep=$2
file=$3
cat <<_EOF_
/* AUTO */
void
$name (int arg)
{
_EOF_
egrep "^#[[:space:]]*define[[:space:]]+"${grep}"[[:space:]]*" \
$include_dir/$file | \
awk '{ printf "\t"; \
if (NR > 1) \
printf "else " ; \
printf "if (arg == %s) \n\t\tprintf(\"%s\");\n", $2, $2 }'
cat <<_EOF_
else /* Should not reach */
(void)printf("<invalid=%ld>", (long)arg);
}
_EOF_
}
# C start
cat <<_EOF_
#include <stdio.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>
#define _KERNEL
#include <sys/socket.h>
#undef _KERNEL
#include <netinet/in.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/ptrace.h>
#include <sys/resource.h>
#include <sys/reboot.h>
#include <sched.h>
#include <sys/linker.h>
#define _KERNEL
#include <sys/thr.h>
#undef _KERNEL
#include <sys/extattr.h>
#include <sys/acl.h>
#include <aio.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/rtprio.h>
#include <sys/shm.h>
#include <nfsserver/nfs.h>
#include <ufs/ufs/quota.h>
#include "kdump_subr.h"
/*
* These are simple support macros. print_or utilizes a variable
* defined in the calling function to track whether or not it should
* print a logical-OR character ('|') before a string. if_print_or
* simply handles the necessary "if" statement used in many lines
* of this file.
*/
#define print_or(str,orflag) do { \\
if (orflag) putchar('|'); else orflag = 1; \\
printf (str); } \\
while (0)
#define if_print_or(i,flag,orflag) do { \\
if ((i & flag) == flag) \\
print_or(#flag,orflag); } \\
while (0)
/* MANUAL */
extern char *signames[]; /* from kdump.c */
void
signame (int sig)
{
if (sig > 0 && sig < NSIG)
(void)printf("SIG%s",signames[sig]);
else
(void)printf("SIG %d", sig);
}
/* MANUAL */
void
semctlname (int cmd)
{
switch (cmd) {
case GETNCNT:
(void)printf("GETNCNT");
break;
case GETPID:
(void)printf("GETPID");
break;
case GETVAL:
(void)printf("GETVAL");
break;
case GETALL:
(void)printf("GETALL");
break;
case GETZCNT:
(void)printf("GETZCNT");
break;
case SETVAL:
(void)printf("SETVAL");
break;
case SETALL:
(void)printf("SETALL");
break;
case IPC_RMID:
(void)printf("IPC_RMID");
break;
case IPC_SET:
(void)printf("IPC_SET");
break;
case IPC_STAT:
(void)printf("IPC_STAT");
break;
default: /* Should not reach */
(void)printf("<invalid=%ld>", (long)cmd);
}
}
/* MANUAL */
void
shmctlname (int cmd) {
switch (cmd) {
case IPC_RMID:
(void)printf("IPC_RMID");
break;
case IPC_SET:
(void)printf("IPC_SET");
break;
case IPC_STAT:
(void)printf("IPC_STAT");
break;
default: /* Should not reach */
(void)printf("<invalid=%ld>", (long)cmd);
}
}
/* MANUAL */
void
semgetname (int flag) {
int or = 0;
if_print_or(flag, SEM_R, or);
if_print_or(flag, SEM_A, or);
if_print_or(flag, (SEM_R>>3), or);
if_print_or(flag, (SEM_A>>3), or);
if_print_or(flag, (SEM_R>>6), or);
if_print_or(flag, (SEM_A>>6), or);
}
/*
* MANUAL
*
* Only used by SYS_open. Unless O_CREAT is set in flags, the
* mode argument is unused (and often bogus and misleading).
*/
void
flagsandmodename (int flags, int mode, int decimal) {
flagsname (flags);
(void)putchar(',');
if ((flags & O_CREAT) == O_CREAT) {
modename (mode);
} else {
if (decimal) {
(void)printf("<unused>%ld", (long)mode);
} else {
(void)printf("<unused>%#lx", (long)mode);
}
}
}
/*
* MANUAL
*
* [g|s]etsockopt's level argument can either be SOL_SOCKET or a value
* referring to a line in /etc/protocols . It might be appropriate
* to use getprotoent(3) here.
*/
void
sockoptlevelname (int level, int decimal)
{
if (level == SOL_SOCKET) {
(void)printf("SOL_SOCKET");
} else {
if (decimal) {
(void)printf("%ld", (long)level);
} else {
(void)printf("%#lx", (long)level);
}
}
}
_EOF_
auto_or_type "modename" "S_[A-Z]+[[:space:]]+[0-6]{7}" "sys/stat.h"
auto_or_type "flagsname" "O_[A-Z]+[[:space:]]+0x[0-9A-Fa-f]+" "sys/fcntl.h"
auto_or_type "accessmodename" "[A-Z]_OK[[:space:]]+0?x?[0-9A-Fa-f]+" "sys/unistd.h"
auto_or_type "mmapprotname" "PROT_[A-Z]+[[:space:]]+0x[0-9A-Fa-f]+" "sys/mman.h"
auto_or_type "mmapflagsname" "MAP_[A-Z]+[[:space:]]+0x[0-9A-Fa-f]+" "sys/mman.h"
auto_or_type "wait4optname" "W[A-Z]+[[:space:]]+[0-9]+" "sys/wait.h"
auto_or_type "getfsstatflagsname" "MNT_[A-Z]+[[:space:]]+[1-9][0-9]*" "sys/mount.h"
auto_or_type "mountflagsname" "MNT_[A-Z]+[[:space:]]+0x[0-9]+" "sys/mount.h"
auto_or_type "rebootoptname" "RB_[A-Z]+[[:space:]]+0x[0-9]+" "sys/reboot.h"
auto_or_type "flockname" "LOCK_[A-Z]+[[:space:]]+0x[0-9]+" "sys/fcntl.h"
auto_or_type "thrcreateflagsname" "THR_[A-Z]+[[:space:]]+0x[0-9]+" "sys/thr.h"
auto_or_type "mlockallname" "MCL_[A-Z]+[[:space:]]+0x[0-9]+" "sys/mman.h"
auto_or_type "shmatname" "SHM_[A-Z]+[[:space:]]+[0-9]{6}+" "sys/shm.h"
auto_or_type "rforkname" "RF[A-Z]+[[:space:]]+\([0-9]+<<[0-9]+\)" "sys/unistd.h"
auto_or_type "nfssvcname" "NFSSVC_[A-Z]+[[:space:]]+0x[0-9]+" "nfsserver/nfs.h"
auto_switch_type "whencename" "SEEK_[A-Z]+[[:space:]]+[0-9]+" "sys/unistd.h"
auto_switch_type "rlimitname" "RLIMIT_[A-Z]+[[:space:]]+[0-9]+" "sys/resource.h"
auto_switch_type "shutdownhowname" "SHUT_[A-Z]+[[:space:]]+0x[0-9]+" "sys/socket.h"
auto_switch_type "prioname" "PRIO_[A-Z]+[[:space:]]+[0-9]" "sys/resource.h"
auto_switch_type "madvisebehavname" "_?MADV_[A-Z]+[[:space:]]+[0-9]+" "sys/mman.h"
auto_switch_type "msyncflagsname" "MS_[A-Z]+[[:space:]]+0x[0-9]+" "sys/mman.h"
auto_switch_type "schedpolicyname" "SCHED_[A-Z]+[[:space:]]+[0-9]+" "sched.h"
auto_switch_type "kldunloadfflagsname" "LINKER_UNLOAD_[A-Z]+[[:space:]]+[0-9]+" "sys/linker.h"
auto_switch_type "extattrctlname" "EXTATTR_NAMESPACE_[A-Z]+[[:space:]]+0x[0-9]+" "sys/extattr.h"
auto_switch_type "kldsymcmdname" "KLDSYM_[A-Z]+[[:space:]]+[0-9]+" "sys/linker.h"
auto_switch_type "sendfileflagsname" "SF_[A-Z]+[[:space:]]+[0-9]+" "sys/socket.h"
auto_switch_type "acltypename" "ACL_TYPE_[A-Z]+[[:space:]]+0x[0-9]+" "sys/acl.h"
auto_switch_type "sigprocmaskhowname" "SIG_[A-Z]+[[:space:]]+[0-9]+" "sys/signal.h"
auto_switch_type "lio_listioname" "LIO_(NO)?WAIT[[:space:]]+[0-9]+" "aio.h"
auto_switch_type "minheritname" "INHERIT_[A-Z]+[[:space:]]+[0-9]+" "sys/mman.h"
auto_switch_type "quotactlname" "Q_[A-Z]+[[:space:]]+0x[0-9]+" "ufs/ufs/quota.h"
auto_if_type "sockdomainname" "PF_[[:alnum:]]+[[:space:]]+" "sys/socket.h"
auto_if_type "sockfamilyname" "AF_[[:alnum:]]+[[:space:]]+" "sys/socket.h"
auto_if_type "sockipprotoname" "IPPROTO_[[:alnum:]]+[[:space:]]+" "netinet/in.h"
auto_switch_type "sockoptname" "SO_[A-Z]+[[:space:]]+0x[0-9]+" "sys/socket.h"
auto_switch_type "socktypename" "SOCK_[A-Z]+[[:space:]]+[1-9]+[0-9]*" "sys/socket.h"
auto_switch_type "ptraceopname" "PT_[[:alnum:]]+[[:space:]]+[0-9]+" "sys/ptrace.h"
cat <<_EOF_
/*
* AUTO - Special
* F_ is used to specify fcntl commands as well as arguments. Both sets are
* grouped in fcntl.h, and this awk script grabs the first group.
*/
void
fcntlcmdname (int cmd, int arg, int decimal)
{
switch (cmd) {
_EOF_
egrep "^#[[:space:]]*define[[:space:]]+F_[A-Z]+[[:space:]]+[0-9]+[[:space:]]*" \
$include_dir/sys/fcntl.h | \
awk 'BEGIN { o=0 } { for (i = 1; i <= NF; i++) \
if ($i ~ /define/) \
break; \
++i; \
if (o <= $(i+1)) \
printf "\tcase %s:\n\t\t(void)printf(\"%s\");\n\t\tbreak;\n", $i, $i; \
else \
exit; \
o = $(i+1) }'
cat <<_EOF_
default: /* Should not reach */
(void)printf("<invalid=%ld>", (long)cmd);
}
(void)putchar(',');
if (cmd == F_GETFD || cmd == F_SETFD) {
if (arg == FD_CLOEXEC)
(void)printf("FD_CLOEXEC");
else if (arg == 0)
(void)printf("0");
else {
if (decimal)
(void)printf("<invalid>%ld", (long)arg);
else
(void)printf("<invalid>%#lx", (long)arg);
}
} else if (cmd == F_SETFL) {
flagsname(arg);
} else {
if (decimal)
(void)printf("%ld", (long)arg);
else
(void)printf("%#lx", (long)arg);
}
}
/*
* AUTO - Special
*
* The only reason this is not fully automated is due to the
* grep -v RTP_PRIO statement. A better egrep line should
* make this capable of being a auto_switch_type() function.
*/
void
rtprioname (int func)
{
switch (func) {
_EOF_
egrep "^#[[:space:]]*define[[:space:]]+RTP_[A-Z]+[[:space:]]+0x[0-9]+[[:space:]]*" \
$include_dir/sys/rtprio.h | grep -v RTP_PRIO | \
awk '{ for (i = 1; i <= NF; i++) \
if ($i ~ /define/) \
break; \
++i; \
printf "\tcase %s:\n\t\t(void)printf(\"%s\");\n\t\tbreak;\n", $i, $i }'
cat <<_EOF_
default: /* Should not reach */
(void)printf("<invalid=%ld>", (long)func);
}
}
/*
* AUTO - Special
*
* The send and recv functions have a flags argument which can be
* set to 0. There is no corresponding #define. The auto_ functions
* detect this as "invalid", which is incorrect here.
*/
void
sendrecvflagsname (int flags)
{
int or = 0;
if (flags == 0) {
(void)printf("0");
return;
}
printf("%#x<", flags);
_EOF_
egrep "^#[[:space:]]*define[[:space:]]+MSG_[A-Z]+[[:space:]]+0x[0-9]+[[:space:]]*" $include_dir/sys/socket.h | \
awk '{ for (i = 1; i <= NF; i++) \
if ($i ~ /define/) \
break; \
++i; \
printf "\tif(!((flags>0)^((%s)>0)))\n\t\tif_print_or(flags, %s, or);\n", $i, $i }'
cat <<_EOF_
printf(">");
}
_EOF_