]> Zhao Yanbai Git Server - minix.git/commitdiff
trace(1): document how to add an IOCTL handler 82/2882/2
authorDavid van Moolenbroek <david@minix3.org>
Tue, 11 Nov 2014 23:11:21 +0000 (23:11 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Wed, 12 Nov 2014 12:02:29 +0000 (12:02 +0000)
Also fix two small IOCTL-related bugs:
- do not print an argument pointer for argument-less IOCTLs;
- print IOCTL contents with -V given once, just like structures.

Change-Id: Iec7373003d71937fd34ee4b9db6c6cec0c916411

minix/usr.bin/trace/NOTES
minix/usr.bin/trace/ioctl.c

index 91d8cb761d439da7957722cd979cce657fca9576..fed4a8475b8aeda5b0176b96e13bb0aa1aa1de30 100644 (file)
@@ -201,6 +201,112 @@ functions for call with no parameters, and for functions which need no more
 than put_result() to print their system call result, respectively.
 
 
+ADDING AN IOCTL HANDLER
+
+There are many IOCTL requests, and many have their own associated data types.
+Like with system calls, the idea is to provide an actual implementation for any
+IOCTLs that can actually occur in the wild.  This consists of printing the full
+IOCTL name, as well as its argument.  First something about how handling IOCTLs
+is grouped into files in the ioctl subdirectory, then about the actual
+procedure the IOCTLs are handled.
+
+Grouping of IOCTL handling in the ioctl subdirectory is currently based on the
+IOCTLs' associated device type.  This is not a performance optimization: for
+any given IOCTL, there is no way for the main IOCTL code (in ioctl.c) to know
+which group, if any, contains a handler for the IOCTL, so it simply queries all
+groups.  The grouping is there only to keep down the size of individual source
+files, and as such not even strict: for example, networking IOCTLs are
+technically a subset of character IOCTLs, and kept separate only because there
+are so many of them.  The point here is mainly that the separation is not at
+all set in stone.  However, the svrctl group is an exception: svrctl(2)
+requests are very much like IOCTLs, and thus also treated as such, but they are
+in a different namespace.  Thus, their handlers are in a separate file.
+
+As per the ioctl_table structure, each group has a function to return the name
+of an IOCTL it knows (typically <group>_ioctl_name), and a function to handle
+IOCTL arguments (typically <group>_ioctl_arg).  Whenever an IOCTL system call
+is made, each group's name function is queried.  This function has the
+following prototype:
+
+  const char *group_ioctl_name(unsigned long req);
+
+The "req" parameter contains the IOCTL request code.  The function is to return
+a static non-NULL string if it knows the name for the request code, or NULL
+otherwise.  If the function returns a non-NULL string, that name will be used
+for the IOCTL.  In addition, if the IOCTL has an argument at all, i.e. it is
+not of the basic _IO() type, that group (and only that group!) will be queried
+about the IOCTL argument, by calling the group's IOCTL argument function.  The
+IOCTL argument function has the following prototype:
+
+  int group_ioctl_arg(struct trace_proc *proc, unsigned long req, void *ptr,
+          int dir);
+
+For a single IOCTL, this function may be called up to three times.  The first
+time, "ptr" will be NULL, and based on the same IOCTL request code "req", the
+function must return any bitwise combination of two flags: IF_OUT and IF_IN.
+
+The returned flags determine whether and how the IOCTL's argument will be
+printed: before and/or after performing the IOCTL system call.  These two flags
+effectively correspond to the "write" and "read" argument directions of IOCTLs:
+IF_OUT indicates that the argument should be printed before the IOCTL request,
+and this is to be used only for IOCTLs of type _IOW() and _IOWR().  IF_IN
+indicates that the argument should be printed after the IOCTL request (but if
+it was successful only), and is to be used only for IOCTLs of type _IOR() and
+_IOWR().
+
+The returned flag combination determines how the IOCTL is formatted.  The
+following possible return values result in the following output formats, again
+with the "|" indicating the call split, "out" being the IOCTL argument contents
+printed before the IOCTL call, and "in" being the IOCTL argument printed after
+the IOCTL call:
+
+  0:             ioctl(3, IOCFOO, &0xaddress) = |0
+  IF_OUT:        ioctl(3, IOCFOO, {out}) = |0
+  IF_OUT|IF_IN:  ioctl(3, IOCFOO, {out}) = |0 {in}
+  IF_IN:         ioctl(3, IOCFOO, |{in}) = 0
+
+Both IF_ flags are optional, mainly because it is not always needed to print
+both sides for an _IOWR() request.  However, using the wrong flag (e.g., IF_OUT
+for an _IOR() request, which simply makes no sense) will trigger an assert.
+Also, the function should basically never return 0 for an IOCTL it recognizes.
+Again, for IOCTLs of type _IO(), which have no argument, the argument function
+is not called at all.
+
+Now the important part.  For each flag that is returned on the initial call to
+the argument function, the argument function will be called again, this time to
+perform actual printing of the argument.  For these subsequent calls, "ptr"
+will point to the argument data which has been copied to the local address
+space, and "dir" will contain one of the returned flags (that is, either IF_OUT
+or IF_IN) to indicate whether the function is called before or after the IOCTL
+call.  As should now be obvious, if the first call returned IF_OUT | IF_IN, the
+function will be called again with "dir" set to IF_OUT, and if the IOCTL call
+did not fail, once more (for the third time), now with "dir" set to IF_IN.
+
+For these calls with an actual "ptr" value and a direction, the function should
+indeed print the argument as appropriate, using "proc" as process pointer for
+use in calls to the printing functions.  The general approach is to print non-
+structure arguments as single values with no field name, and structure
+arguments by printing its fields with their field names.  The main code (in
+ioctl.c) ensures that the output is enclosed in curly brackets, thus making the
+output look like a structure anyway.
+
+For these subsequent calls, the argument function's return value should be
+IF_ALL if all parts of the IOCTL argument have been printed, or 0 otherwise.
+In the latter case, the main code will add a final ".." field to indicate to
+the user that not all parts of the argument have been printed, very much like
+the "all" parameter of put_close_struct.
+
+If no name can be found for the IOCTL request code, the argument will simply be
+printed as a pointer.  The same happens in error cases, for example if copying
+in the IOCTL data resulted in an error.
+
+There is no support for dealing with multiple IOCTLs with the exact same
+request code--something that should not, but sadly does, occur in practice.
+For now, the preferred approach would be to implement only support for the
+IOCTL that is most likely to be found in practice, and possibly to put a horse
+head in the bed of whoever introduced the duplicate request code.
+
+
 INTERNALS: MULTIPROCESS OUTPUT AND PREEMPTION
 
 Things get interesting when multiple processes are traced at once.  Due to the
index ed8931bafa05dd44e44a8958dbc69831338d9345..4c4b49834bcbf10da33c95eff61bff503f3be15b 100644 (file)
@@ -44,16 +44,19 @@ put_ioctl_req(struct trace_proc * proc, const char * name, unsigned long req,
         * smart about looking up particular codes in each switch statement,
         * although in the worst case, it's a full O(n) lookup.
         */
-       for (i = 0; !valuesonly && i < COUNT(ioctl_table); i++) {
+       for (i = 0; i < COUNT(ioctl_table); i++) {
                /* IOCTLs and SVRCTLs are considered different name spaces. */
                if (ioctl_table[i].is_svrctl != is_svrctl)
                        continue;
 
                if ((text = ioctl_table[i].name(req)) != NULL) {
-                       put_field(proc, name, text);
-
                        proc->ioctl_index = i;
 
+                       if (valuesonly)
+                               break;
+
+                       put_field(proc, name, text);
+
                        return;
                }
        }
@@ -106,9 +109,12 @@ put_ioctl_arg_out(struct trace_proc * proc, const char * name,
        dir = (_MINIX_IOCTL_IOW(req) ? IF_OUT : 0) |
            (_MINIX_IOCTL_IOR(req) ? IF_IN : 0);
 
-       if (dir == 0)
+       if (dir == 0) {
                proc->ioctl_index = -1; /* no argument to print at all */
 
+               return CT_DONE;
+       }
+
        /* No support for printing big-IOCTL contents just yet. */
        if (valuesonly > 1 || _MINIX_IOCTL_BIG(req) ||
            proc->ioctl_index == -1) {