On Fri, Dec 09, 2022 at 12:43:31PM -0600, Scott Cheloha wrote: > On Fri, Dec 09, 2022 at 12:10:59PM +0100, Alexandre Ratchov wrote: > > This diff adds an option to display variables periodically. Basically > > it replaces this usage: > > > > while sleep 1; do audioctl play.errors; done > > > > by > > > > audioctl -w 1 play.errors > > > > The purpose of above audioctl commands is to debug underruns, so we > > don't want to fork a new process and reopen the device. This would > > trigger longer kernel code-paths and may cause additional underruns > > than the ones being investigated. > > > > OK? > > I like the idea, but I would like to tweak some things. > > - Add [-w wait] to the first synoptic form in the manpage. It's legal > to do e.g. > > # audioctl -w 1 >
done > - Call the variable "wait" in audioctl.c to match the manpage. > done (used wait_sec, as there's a global wait() symbol). > - Update usagestr to mention [-w wait]. > done > - When polling variables periodically, it's better to use setitimer(2) > and sigsuspend(2) instead of sleep(3). setitimer(2) keeps the period > from drifting. > > - Let the user SIGINT (^C) out of the program without returning an > error to the shell. > > I'm unsure about this one, but it seems logical to give the user a > way to gracefully terminate the program. You say in the manpage that > the program will continue printing until it is interrupted. > I just tried these. Synchronizing the display to a clock might make sense if it was the sound card's clock, but here the result was boiler with no benefit. The intent of -w is to just show the variables from time to time, so keeping the code trivial is more important, IMHO. I've added a comment to say so. About ^C, I've changed the man page text to "audioctl will display variables forever." which implies that ^C is out of the scope. Index: audioctl.8 =================================================================== RCS file: /cvs/src/usr.bin/audioctl/audioctl.8,v retrieving revision 1.4 diff -u -p -r1.4 audioctl.8 --- audioctl.8 23 Apr 2020 00:16:59 -0000 1.4 +++ audioctl.8 10 Dec 2022 05:50:05 -0000 @@ -35,9 +35,11 @@ .Sh SYNOPSIS .Nm audioctl .Op Fl f Ar file +.Op Fl w Ar wait .Nm audioctl .Op Fl n .Op Fl f Ar file +.Op Fl w Ar wait .Ar name ... .Nm audioctl .Op Fl nq @@ -59,6 +61,12 @@ The default is Suppress printing of the variable name. .It Fl q Suppress all output when setting a variable. +.It Fl w Ar wait +Pause +.Ar wait +seconds between each display. +.Nm +will display variables forever. .It Ar name Ns = Ns Ar value Attempt to set the specified variable .Ar name @@ -130,10 +138,10 @@ audio control devices audio devices .El .Sh EXAMPLES -Display the number of bytes of silence inserted during play buffer -underruns since device started: +Once per-second, display the number of bytes of silence inserted due to buffer +underruns (since the device started playback): .Bd -literal -offset indent -# audioctl play.errors +# audioctl -w 1 play.errors .Ed .Pp Use signed 24-bit samples and 44100Hz sample rate: Index: audioctl.c =================================================================== RCS file: /cvs/src/usr.bin/audioctl/audioctl.c,v retrieving revision 1.43 diff -u -p -r1.43 audioctl.c --- audioctl.c 12 Jul 2021 15:09:19 -0000 1.43 +++ audioctl.c 10 Dec 2022 05:50:05 -0000 @@ -43,6 +43,7 @@ struct field { #define STR 2 #define ENC 3 int type; + int show; int set; } fields[] = { {"name", &rname.name, NULL, STR}, @@ -63,11 +64,11 @@ struct field { }; const char usagestr[] = - "usage: audioctl [-f file]\n" - " audioctl [-n] [-f file] name ...\n" + "usage: audioctl [-f file] [-w wait_sec]\n" + " audioctl [-n] [-f file] [-w wait_sec] name ...\n" " audioctl [-nq] [-f file] name=value ...\n"; -int fd, show_names = 1, quiet = 0; +int fd, show_names = 1, quiet = 0, wait_sec = 0; /* * parse encoding string (examples: s8, u8, s16, s16le, s24be ...) @@ -198,20 +199,9 @@ audio_main(int argc, char **argv) char *lhs, *rhs; int set = 0; - if (ioctl(fd, AUDIO_GETSTATUS, &rstatus) == -1) - err(1, "AUDIO_GETSTATUS"); - if (ioctl(fd, AUDIO_GETDEV, &rname) == -1) - err(1, "AUDIO_GETDEV"); - if (ioctl(fd, AUDIO_GETPAR, &rpar) == -1) - err(1, "AUDIO_GETPAR"); - if (ioctl(fd, AUDIO_GETPOS, &rpos) == -1) - err(1, "AUDIO_GETPOS"); if (argc == 0) { - for (f = fields; f->name != NULL; f++) { - printf("%s=", f->name); - print_field(f, f->raddr); - printf("\n"); - } + for (f = fields; f->name != NULL; f++) + f->show = 1; } AUDIO_INITPAR(&wpar); for (; argc > 0; argc--, argv++) { @@ -231,15 +221,41 @@ audio_main(int argc, char **argv) parse_field(f, f->waddr, rhs); f->set = 1; set = 1; - } else { + } else + f->show = 1; + } + + if (set && wait_sec) + errx(1, "Can't set variables wait_secically"); + + while (1) { + if (ioctl(fd, AUDIO_GETSTATUS, &rstatus) == -1) + err(1, "AUDIO_GETSTATUS"); + if (ioctl(fd, AUDIO_GETDEV, &rname) == -1) + err(1, "AUDIO_GETDEV"); + if (ioctl(fd, AUDIO_GETPAR, &rpar) == -1) + err(1, "AUDIO_GETPAR"); + if (ioctl(fd, AUDIO_GETPOS, &rpos) == -1) + err(1, "AUDIO_GETPOS"); + for (f = fields; f->name != NULL; f++) { + if (!f->show) + continue; if (show_names) printf("%s=", f->name); print_field(f, f->raddr); printf("\n"); } + + if (wait_sec == 0) + break; + + /* ioctls are fast, we neglect drift from real-time clock */ + sleep(wait_sec); } + if (!set) return; + if (ioctl(fd, AUDIO_SETPAR, &wpar) == -1) err(1, "AUDIO_SETPAR"); if (ioctl(fd, AUDIO_GETPAR, &wpar) == -1) @@ -261,9 +277,10 @@ int main(int argc, char **argv) { char *path = "/dev/audioctl0"; + const char *errstr; int c; - while ((c = getopt(argc, argv, "anf:q")) != -1) { + while ((c = getopt(argc, argv, "anf:qw:")) != -1) { switch (c) { case 'a': /* ignored, compat */ break; @@ -275,6 +292,11 @@ main(int argc, char **argv) break; case 'q': quiet = 1; + break; + case 'w': + wait_sec = strtonum(optarg, 1, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "wait is %s: %s", errstr, optarg); break; default: fputs(usagestr, stderr);