Scott Haseley 0ab862abfa 1) Moved tprintf and related functions to their own file in common/libc/printf/tprintf.c.
This file is included by both libc and hostcompat.

2) Changed printf -> tprintf in all testbin programs
2016-01-15 13:33:11 -05:00

269 lines
6.5 KiB
C

/*
* Copyright (c) 2014
* The President and Fellows of Harvard College.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* multiexec - stuff N procs into exec at once
* usage: multiexec [-j N] [prog [arg...]]
*
* This can be used both to see what happens when you have a lot of
* execs at once (its original purpose) by running ordinary programs
* like pwd (the default) and also just as a workload generator /
* convenient way to start lots of copies of things at once.
*
* Note that this uses execv directly (not execvp) so it doesn't
* search $PATH for the program you want to run, and therefore it
* needs full paths. One could make it use execvp; it doesn't because
* that would complicate its coordinated startup logic, and also get
* in the way of using it to debug execv.
*
* Some things to try:
* multiexec /bin/true
* multiexec /bin/cat foo (for some file foo)
* multiexec /testbin/add 3 8
* Some more aggressive things to try:
* multiexec /testbin/factorial 15
* multiexec /testbin/bigexec
* multiexec /testbin/sort (once you have a VM system)
* Some mean things:
* multiexec /testbin/forktest
* multiexec /testbin/bloat (once you have sbrk)
* multiexec /bin/sh (this makes a huge mess unless you have job control)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <err.h>
////////////////////////////////////////////////////////////
// semaphores
/*
* We open the semaphore separately in each process to avoid
* filehandle-level locking problems. If you can't be "reading" and
* "writing" the semaphore concurrently because of the open file
* object lock, then using the same file handle for P and V will
* deadlock. Also, if this same lock is used to protect the reference
* count on the open file logic, fork will block if another process is
* using the same file handle for P, and then we're deadlocked too.
*
* Ideally the open file / filetable code wouldn't have this problem,
* as it makes e.g. console output from background jobs behave
* strangely, but it's a common issue in practice and it's better for
* tests to be immune to it.
*/
struct usem {
char name[32];
int fd;
};
static
void
semcreate(const char *tag, struct usem *sem)
{
int fd;
snprintf(sem->name, sizeof(sem->name), "sem:multiexec.%s.%d",
tag, (int)getpid());
fd = open(sem->name, O_WRONLY|O_CREAT|O_TRUNC, 0664);
if (fd < 0) {
err(1, "%s: create", sem->name);
}
close(fd);
}
static
void
semopen(struct usem *sem)
{
sem->fd = open(sem->name, O_RDWR, 0664);
if (sem->fd < 0) {
err(1, "%s: open", sem->name);
}
}
static
void
semclose(struct usem *sem)
{
close(sem->fd);
}
static
void
semdestroy(struct usem *sem)
{
remove(sem->name);
}
static
void
semP(struct usem *sem, size_t num)
{
char c[num];
if (read(sem->fd, c, num) < 0) {
err(1, "%s: read", sem->name);
}
}
static
void
semV(struct usem *sem, size_t num)
{
char c[num];
/* semfs does not use these values, but be conservative */
memset(c, 0, num);
if (write(sem->fd, c, num) < 0) {
err(1, "%s: write", sem->name);
}
}
////////////////////////////////////////////////////////////
// test
#define SUBARGC_MAX 64
static char *subargv[SUBARGC_MAX];
static int subargc = 0;
static
void
spawn(int njobs)
{
struct usem s1, s2;
pid_t pids[njobs];
int failed, status;
int i;
semcreate("1", &s1);
semcreate("2", &s2);
tprintf("Forking %d child processes...\n", njobs);
for (i=0; i<njobs; i++) {
pids[i] = fork();
if (pids[i] == -1) {
/* abandon the other procs; no way to kill them */
err(1, "fork");
}
if (pids[i] == 0) {
/* child */
semopen(&s1);
semopen(&s2);
semV(&s1, 1);
semP(&s2, 1);
semclose(&s1);
semclose(&s2);
execv(subargv[0], subargv);
warn("execv: %s", subargv[0]);
_exit(1);
}
}
semopen(&s1);
semopen(&s2);
tprintf("Waiting for fork...\n");
semP(&s1, njobs);
tprintf("Starting the execs...\n");
semV(&s2, njobs);
failed = 0;
for (i=0; i<njobs; i++) {
if (waitpid(pids[i], &status, 0) < 0) {
warn("waitpid");
failed++;
}
else if (WIFSIGNALED(status)) {
warnx("pid %d (child %d): Signal %d",
(int)pids[i], i, WTERMSIG(status));
failed++;
}
else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
warnx("pid %d (child %d): Exit %d",
(int)pids[i], i, WEXITSTATUS(status));
failed++;
}
}
if (failed > 0) {
warnx("%d children failed", failed);
}
else {
tprintf("Succeeded\n");
}
semclose(&s1);
semclose(&s2);
semdestroy(&s1);
semdestroy(&s2);
}
int
main(int argc, char *argv[])
{
static char default_prog[] = "/bin/pwd";
int njobs = 12;
int i;
for (i=1; i<argc; i++) {
if (!strcmp(argv[i], "-j")) {
i++;
if (argv[i] == NULL) {
errx(1, "Option -j requires an argument");
}
njobs = atoi(argv[i]);
}
#if 0 /* XXX we apparently don't have strncmp? */
else if (!strncmp(argv[i], "-j", 2)) {
njobs = atoi(argv[i] + 2);
}
#endif
else {
subargv[subargc++] = argv[i];
if (subargc >= SUBARGC_MAX) {
errx(1, "Too many arguments");
}
}
}
if (subargc == 0) {
subargv[subargc++] = default_prog;
}
subargv[subargc] = NULL;
spawn(njobs);
return 0;
}