backup
This commit is contained in:
commit
64ae6749d1
28
LICENSE
Normal file
28
LICENSE
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2023, Martin Hart
|
||||||
|
|
||||||
|
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 copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
|
19
Makefile
Normal file
19
Makefile
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
TARGET := smash-MPI.so
|
||||||
|
CC := mpicc
|
||||||
|
CFLAGS := -std=gnu99 -fPIC -Wall -Wextra -c -I.
|
||||||
|
LIB := -ldl
|
||||||
|
SRC := $(wildcard *.c)
|
||||||
|
OBJ := $(SRC:.c=.o)
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := $(TARGET)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
|
||||||
|
$(TARGET): $(OBJ)
|
||||||
|
$(CC) $(LIB) -shared $? -o $@
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(CFLAGS) $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@rm -f $(OBJ) $(TARGET)
|
121
callout.c
Normal file
121
callout.c
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#include "callout.h"
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
struct callo callout[NCALL] = { 0 };
|
||||||
|
extern timer_t smash_timer_id;
|
||||||
|
|
||||||
|
void
|
||||||
|
smash_print_callout(void)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; callout[i].c_func != NULL; ++i)
|
||||||
|
printf("c_time: %lli\nc_arg: %i\nc_func @ %p\n\n",
|
||||||
|
callout[i].c_time, callout[i].c_arg,
|
||||||
|
(void *)callout[i].c_func);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
smash_set_timer(int time)
|
||||||
|
{
|
||||||
|
int t, s, ns;
|
||||||
|
struct itimerspec curr;
|
||||||
|
|
||||||
|
timer_gettime(smash_timer_id, &curr);
|
||||||
|
|
||||||
|
if (time == -1 || (curr.it_value.tv_nsec == 0 && curr.it_value.tv_sec == 0)) {
|
||||||
|
t = time == -1 ? callout[0].c_time : time;
|
||||||
|
s = t / 1000;
|
||||||
|
ns = (t % 1000) * 1000000;
|
||||||
|
|
||||||
|
if (s == 0 && ns == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct itimerspec its;
|
||||||
|
|
||||||
|
its.it_value.tv_sec = s;
|
||||||
|
its.it_value.tv_nsec = ns;
|
||||||
|
|
||||||
|
its.it_interval.tv_sec = 0;
|
||||||
|
its.it_interval.tv_nsec = 0;
|
||||||
|
if (timer_settime(smash_timer_id, 0, &its, NULL) < 0)
|
||||||
|
err(1, "timer_settime");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sem_t *
|
||||||
|
smash_timeout(int (*func)(), int arg, int time, struct mpi_send_args *args)
|
||||||
|
{
|
||||||
|
int t;
|
||||||
|
struct callo *p1, *p2;
|
||||||
|
|
||||||
|
|
||||||
|
t = time;
|
||||||
|
p1 = &callout[0];
|
||||||
|
while (p1->c_func != 0 && p1->c_time <= t) {
|
||||||
|
t -= p1->c_time;
|
||||||
|
p1++;
|
||||||
|
}
|
||||||
|
p1->c_time -= t;
|
||||||
|
p2 = p1;
|
||||||
|
while (p2->c_func != 0)
|
||||||
|
p2++;
|
||||||
|
while (p2 >= p1) {
|
||||||
|
(p2+1)->c_time = p2->c_time;
|
||||||
|
(p2+1)->c_func = p2->c_func;
|
||||||
|
(p2+1)->c_arg = p2->c_arg;
|
||||||
|
(p2+1)->c_send_args = p2->c_send_args;
|
||||||
|
p2--;
|
||||||
|
}
|
||||||
|
p1->c_time = t;
|
||||||
|
p1->c_func = func;
|
||||||
|
p1->c_arg = arg;
|
||||||
|
if (args != NULL)
|
||||||
|
memcpy(&p1->c_send_args, args, sizeof(struct mpi_send_args));
|
||||||
|
|
||||||
|
smash_set_timer(t);
|
||||||
|
|
||||||
|
return &p1->c_lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
smash_clock(void)
|
||||||
|
{
|
||||||
|
struct callo *p1, *p2;
|
||||||
|
|
||||||
|
if (callout[0].c_func != 0) {
|
||||||
|
p1 = p2 = &callout[0];
|
||||||
|
p1->c_time = 0;
|
||||||
|
|
||||||
|
while (p1->c_func != 0 && p1->c_time == 0) {
|
||||||
|
switch (p1->c_arg) {
|
||||||
|
case 6:
|
||||||
|
p1->c_func(p1->c_send_args.buf, p1->c_send_args.count,
|
||||||
|
p1->c_send_args.datatype, p1->c_send_args.dest,
|
||||||
|
p1->c_send_args.tag, p1->c_send_args.comm);
|
||||||
|
free(p1->c_send_args.buf);
|
||||||
|
sem_post(&p1->c_lock);
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
p1->c_func();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p1++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((p2->c_func = p1->c_func)) {
|
||||||
|
p2->c_time = p1->c_time;
|
||||||
|
p2->c_arg = p1->c_arg;
|
||||||
|
p2->c_send_args = p1->c_send_args;
|
||||||
|
p1++;
|
||||||
|
p2++;
|
||||||
|
}
|
||||||
|
smash_set_timer(-1);
|
||||||
|
}
|
||||||
|
}
|
31
callout.h
Normal file
31
callout.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef CALLOUT_H
|
||||||
|
#define CALLOUT_H
|
||||||
|
|
||||||
|
#define NCALL 256
|
||||||
|
|
||||||
|
#include <mpi.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
|
struct mpi_send_args {
|
||||||
|
void *buf;
|
||||||
|
MPI_Comm comm;
|
||||||
|
MPI_Datatype datatype;
|
||||||
|
int count, dest, tag;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct callo {
|
||||||
|
long long c_time; /* incremental time */
|
||||||
|
int c_arg; /* argument to routine */
|
||||||
|
int (*c_func)(); /* routine */
|
||||||
|
sem_t c_lock; /* lock to preserve locking semantic */
|
||||||
|
struct mpi_send_args c_send_args; /* args for MPI_Send */
|
||||||
|
};
|
||||||
|
|
||||||
|
void smash_print_callout(void);
|
||||||
|
|
||||||
|
sem_t *smash_timeout(int (*func)(), int arg, int time,
|
||||||
|
struct mpi_send_args *args);
|
||||||
|
|
||||||
|
void smash_clock(void);
|
||||||
|
|
||||||
|
#endif /* CALLOUT_H */
|
310
hooking.c
Normal file
310
hooking.c
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
#include <dlfcn.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <gnu/lib-names.h>
|
||||||
|
#include <mpi.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "callout.h"
|
||||||
|
#include "hooking.h"
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
#define SMASH_GRAPH 0x1234
|
||||||
|
|
||||||
|
timer_t smash_timer_id;
|
||||||
|
unsigned int smash_my_rank;
|
||||||
|
int smash_dead, smash_world_size, smash_alarm;
|
||||||
|
|
||||||
|
struct cfg_delays *smash_delays;
|
||||||
|
struct cfg_failures *smash_failures;
|
||||||
|
|
||||||
|
struct smash_graph_msg {
|
||||||
|
int src, dst;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct smash_graph_msgs {
|
||||||
|
size_t i;
|
||||||
|
struct smash_graph_msg msgs[4096];
|
||||||
|
} smash_graph_msgs;
|
||||||
|
|
||||||
|
static int master_done = 0;
|
||||||
|
|
||||||
|
int
|
||||||
|
smash_failure(void)
|
||||||
|
{
|
||||||
|
int buf;
|
||||||
|
MPI_Status status;
|
||||||
|
size_t recv = 0;
|
||||||
|
int (*f)();
|
||||||
|
|
||||||
|
smash_dead = 1;
|
||||||
|
f = smash_get_lib_func(LIBMPI, "MPI_Recv");
|
||||||
|
while (recv != smash_world_size - smash_failures->size) {
|
||||||
|
f(&buf, 1, MPI_INT, MPI_ANY_SOURCE, 0xdead, MPI_COMM_WORLD, &status);
|
||||||
|
recv++;
|
||||||
|
}
|
||||||
|
MPI_Finalize();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag,
|
||||||
|
MPI_Comm comm, MPI_Status *status) {
|
||||||
|
int (*f)(), res;
|
||||||
|
|
||||||
|
f = smash_get_lib_func(LIBMPI, "MPI_Recv");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
res = f(buf, count, datatype, source, tag, comm, status);
|
||||||
|
if (status->MPI_TAG != 0xdead || status->MPI_TAG != SMASH_GRAPH)
|
||||||
|
break;
|
||||||
|
bzero(status, sizeof(MPI_Status));
|
||||||
|
master_done = status->MPI_TAG == SMASH_GRAPH;
|
||||||
|
}
|
||||||
|
|
||||||
|
smash_graph_msgs.msgs[smash_graph_msgs.i].src = status->MPI_SOURCE;
|
||||||
|
smash_graph_msgs.msgs[smash_graph_msgs.i].dst = smash_my_rank;
|
||||||
|
smash_graph_msgs.i++;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
smash_get_lib_func(const char *lname, const char *fname)
|
||||||
|
{
|
||||||
|
void *lib, *p;
|
||||||
|
|
||||||
|
if (!(lib = dlopen(lname, RTLD_LAZY)))
|
||||||
|
errx(EXIT_FAILURE, "%s", dlerror());
|
||||||
|
|
||||||
|
if (!(p = dlsym(lib, fname)))
|
||||||
|
errx(EXIT_FAILURE, "%s", dlerror());
|
||||||
|
|
||||||
|
dlclose(lib);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
smash_handler(__attribute__((unused)) int signum)
|
||||||
|
{
|
||||||
|
smash_clock();
|
||||||
|
}
|
||||||
|
|
||||||
|
timer_t
|
||||||
|
smash_setup_alarm(void)
|
||||||
|
{
|
||||||
|
timer_t timerid;
|
||||||
|
struct sigaction sa;
|
||||||
|
struct sigevent sev;
|
||||||
|
|
||||||
|
sa.sa_handler = smash_handler;
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_flags = SA_RESTART;
|
||||||
|
sigaction(SIGALRM, &sa, NULL);
|
||||||
|
|
||||||
|
sev.sigev_notify = SIGEV_SIGNAL;
|
||||||
|
sev.sigev_signo = SIGALRM;
|
||||||
|
sev.sigev_value.sival_ptr = &timerid;
|
||||||
|
if (timer_create(CLOCK_REALTIME, &sev, &timerid) < 0)
|
||||||
|
errx(1, "timer_create");
|
||||||
|
|
||||||
|
return timerid;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
__libc_start_main(
|
||||||
|
int (*main)(int, char **, char **),
|
||||||
|
int argc,
|
||||||
|
char **argv,
|
||||||
|
int (*init)(int, char **, char **),
|
||||||
|
void (*fini)(void),
|
||||||
|
void (*rtld_fini)(void),
|
||||||
|
void *stack_end)
|
||||||
|
{
|
||||||
|
int (*f)();
|
||||||
|
|
||||||
|
if (smash_parse_cfg(CFG_DELAY, (void **)&smash_delays) < 0)
|
||||||
|
errx(EXIT_FAILURE, "error in CFG_DELAY\n");
|
||||||
|
|
||||||
|
if (smash_parse_cfg(CFG_FAILURE, (void **)&smash_failures) < 0)
|
||||||
|
errx(EXIT_FAILURE, "error in CFG_FAILURE\n");
|
||||||
|
|
||||||
|
f = smash_get_lib_func(LIBSTD, "__libc_start_main");
|
||||||
|
smash_alarm = 0;
|
||||||
|
smash_dead = 0;
|
||||||
|
return f(main, argc, argv, init, fini, rtld_fini, stack_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
MPI_Init(int *argc, char ***argv)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
int (*f)(int *, char ***), res, rank;
|
||||||
|
|
||||||
|
if (!smash_alarm) {
|
||||||
|
smash_timer_id = smash_setup_alarm();
|
||||||
|
smash_alarm = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
smash_graph_msgs.i = 0;
|
||||||
|
|
||||||
|
f = smash_get_lib_func(LIBMPI, "MPI_Init");
|
||||||
|
res = f(argc, argv);
|
||||||
|
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
|
||||||
|
smash_my_rank = rank;
|
||||||
|
MPI_Comm_size(MPI_COMM_WORLD, &smash_world_size);
|
||||||
|
|
||||||
|
if (smash_failures != NULL) {
|
||||||
|
for (i = 0; i < smash_failures->size; ++i) {
|
||||||
|
if (smash_failures->failures[i].node == smash_my_rank) {
|
||||||
|
smash_timeout(smash_failure, 0, smash_failures->failures[i].time, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
save_graph(struct smash_graph_msgs *m)
|
||||||
|
{
|
||||||
|
FILE *fs;
|
||||||
|
size_t i;
|
||||||
|
char *filepath;
|
||||||
|
|
||||||
|
filepath = getenv("SMASH_MPI_GRAPH");
|
||||||
|
if (!filepath)
|
||||||
|
filepath = "graph.dot";
|
||||||
|
|
||||||
|
if (!(fs = fopen(filepath, "w+")))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
fprintf(fs, "digraph SMASH_MPI {\n layout=twopi\n ranksep=3;\n ratio=auto;\n");
|
||||||
|
for (i = 0; i < m->i; ++i) {
|
||||||
|
fprintf(fs, "\"p%d\" -> \"p%d\" [ color=\"purple\" ];\n",
|
||||||
|
m->msgs[i].src,
|
||||||
|
m->msgs[i].dst);
|
||||||
|
}
|
||||||
|
fprintf(fs, "}");
|
||||||
|
fflush(fs);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
MPI_Finalize(void)
|
||||||
|
{
|
||||||
|
int (*f)(void);
|
||||||
|
size_t i, j;
|
||||||
|
int (*ssend)();
|
||||||
|
int (*recv)();
|
||||||
|
|
||||||
|
recv = smash_get_lib_func(LIBMPI, "MPI_Recv");
|
||||||
|
ssend = smash_get_lib_func(LIBMPI, "MPI_Ssend");
|
||||||
|
|
||||||
|
if (smash_failures != NULL) {
|
||||||
|
if (!smash_dead) {
|
||||||
|
for (i = 0; i < smash_failures->size; i++)
|
||||||
|
ssend(&smash_world_size, 1, MPI_INT, smash_failures->failures[i].node, 0xdead, MPI_COMM_WORLD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int done;
|
||||||
|
if (smash_my_rank == 0) {
|
||||||
|
struct smash_graph_msgs tmp = {0};
|
||||||
|
MPI_Status status;
|
||||||
|
for (i = 1; i < (unsigned int)smash_world_size; ++i) {
|
||||||
|
done = 1;
|
||||||
|
ssend(&done, 1, MPI_INT, i, SMASH_GRAPH, MPI_COMM_WORLD, &status);
|
||||||
|
recv(&tmp, sizeof(struct smash_graph_msgs), MPI_CHAR,
|
||||||
|
i, SMASH_GRAPH, MPI_COMM_WORLD,
|
||||||
|
&status);
|
||||||
|
|
||||||
|
for (j = 0; j < tmp.i; ++j) {
|
||||||
|
smash_graph_msgs.msgs[smash_graph_msgs.i].src = tmp.msgs[j].src;
|
||||||
|
smash_graph_msgs.msgs[smash_graph_msgs.i].dst = tmp.msgs[j].dst;
|
||||||
|
smash_graph_msgs.i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Output graph */
|
||||||
|
save_graph(&smash_graph_msgs);
|
||||||
|
} else {
|
||||||
|
if (!master_done)
|
||||||
|
recv(&done, 1, MPI_INT, 0, SMASH_GRAPH, MPI_COMM_WORLD);
|
||||||
|
ssend(&smash_graph_msgs, sizeof(struct smash_graph_msgs),
|
||||||
|
MPI_CHAR, 0, SMASH_GRAPH, MPI_COMM_WORLD);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(smash_delays);
|
||||||
|
free(smash_failures);
|
||||||
|
f = smash_get_lib_func(LIBMPI, "MPI_Finalize");
|
||||||
|
return f();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
MPI_Ssend(const void *buf, int count, MPI_Datatype datatype, int dest,
|
||||||
|
int tag, MPI_Comm comm)
|
||||||
|
{
|
||||||
|
int (*f)();
|
||||||
|
unsigned int i;
|
||||||
|
struct mpi_send_args args = {
|
||||||
|
.count = count,
|
||||||
|
.datatype = datatype,
|
||||||
|
.dest = dest,
|
||||||
|
.tag = tag,
|
||||||
|
.comm = comm,
|
||||||
|
};
|
||||||
|
args.buf = malloc(sizeof(buf) * count);
|
||||||
|
memcpy(args.buf, buf, sizeof(buf) * count);
|
||||||
|
|
||||||
|
f = smash_get_lib_func(LIBMPI, "MPI_Ssend");
|
||||||
|
|
||||||
|
for (i = 0; i < smash_delays->size; ++i) {
|
||||||
|
/* If a delay in the config file matches our rank and the target rank, inject it in the callout struct. */
|
||||||
|
if (smash_delays->delays[i].dst == (unsigned int)dest &&
|
||||||
|
smash_delays->delays[i].src == smash_my_rank &&
|
||||||
|
(smash_delays->delays[i].msg > 0 ||
|
||||||
|
smash_delays->delays[i].msg == -1)) {
|
||||||
|
sem_wait(smash_timeout(f, 6, smash_delays->delays[i].delay, &args));
|
||||||
|
smash_delays->delays[i].msg -= 1 * (smash_delays->delays[i].msg != -1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* If there is no delay to apply, call MPI_Ssend directly. */
|
||||||
|
return f(buf, count, datatype, dest, tag, comm);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest,
|
||||||
|
int tag, MPI_Comm comm)
|
||||||
|
{
|
||||||
|
int (*f)();
|
||||||
|
unsigned int i;
|
||||||
|
struct mpi_send_args args = {
|
||||||
|
.count = count,
|
||||||
|
.datatype = datatype,
|
||||||
|
.dest = dest,
|
||||||
|
.tag = tag,
|
||||||
|
.comm = comm,
|
||||||
|
};
|
||||||
|
args.buf = malloc(sizeof(buf) * count);
|
||||||
|
memcpy(args.buf, buf, sizeof(buf) * count);
|
||||||
|
|
||||||
|
f = smash_get_lib_func(LIBMPI, "MPI_Send");
|
||||||
|
|
||||||
|
for (i = 0; i < smash_delays->size; ++i) {
|
||||||
|
/* If a delay in the config file matches our rank and the target rank, inject it in the callout struct. */
|
||||||
|
if (smash_delays->delays[i].dst == (unsigned int)dest &&
|
||||||
|
smash_delays->delays[i].src == smash_my_rank &&
|
||||||
|
(smash_delays->delays[i].msg > 0 ||
|
||||||
|
smash_delays->delays[i].msg == -1)) {
|
||||||
|
smash_timeout(f, 6, smash_delays->delays[i].delay, &args);
|
||||||
|
smash_delays->delays[i].msg -= 1 * (smash_delays->delays[i].msg != -1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* If there is no delay to apply, call MPI_Send directly. */
|
||||||
|
return f(buf, count, datatype, dest, tag, comm);
|
||||||
|
}
|
14
hooking.h
Normal file
14
hooking.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef HOOKING_H
|
||||||
|
#define HOOKING_H
|
||||||
|
|
||||||
|
#define LIBMPI "libmpi.so"
|
||||||
|
#define LIBSTD LIBM_SO
|
||||||
|
|
||||||
|
/*
|
||||||
|
* smash_get_lib_func takes a null-terminated library name and a
|
||||||
|
* null-terminated symbol name. On success returns the address where the symbol
|
||||||
|
* is loaded in memory, on failure terminates the process.
|
||||||
|
*/
|
||||||
|
void *smash_get_lib_func(const char *lname, const char *fname);
|
||||||
|
|
||||||
|
#endif /* HOOKING_H */
|
141
parser.c
Normal file
141
parser.c
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <regex.h>
|
||||||
|
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
static void *
|
||||||
|
smash_load_file_in_memory(const char *filename, size_t *data_size)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
char *data;
|
||||||
|
struct stat finfo;
|
||||||
|
|
||||||
|
if ((fd = open(filename, O_RDONLY, S_IRUSR | S_IWUSR)) < 0)
|
||||||
|
goto err1;
|
||||||
|
|
||||||
|
if (fstat(fd, &finfo) < 0)
|
||||||
|
goto err2;
|
||||||
|
|
||||||
|
if ((data = mmap(NULL, finfo.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0))
|
||||||
|
== MAP_FAILED)
|
||||||
|
goto err2;
|
||||||
|
|
||||||
|
*data_size = finfo.st_size;
|
||||||
|
close(fd);
|
||||||
|
return data;
|
||||||
|
err2:
|
||||||
|
close(fd);
|
||||||
|
err1:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
smash_populate_delay(const char *line, size_t n, const regmatch_t *rm, struct cfg_delays *delays)
|
||||||
|
{
|
||||||
|
delays->delays[n].src = strtol(line + rm[1].rm_so, NULL, 10);
|
||||||
|
delays->delays[n].dst = strtol(line + rm[2].rm_so, NULL, 10);
|
||||||
|
delays->delays[n].delay = strtol(line + rm[3].rm_so, NULL, 10);
|
||||||
|
delays->delays[n].msg = strtol(line + rm[4].rm_so, NULL, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
smash_populate_failure(const char *line, size_t n, const regmatch_t *rm, struct cfg_failures *failures)
|
||||||
|
{
|
||||||
|
failures->failures[n].node = strtol(line + rm[1].rm_so, NULL, 10);
|
||||||
|
failures->failures[n].time = strtol(line + rm[2].rm_so, NULL, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
smash_get_config_path(enum CFG ctype)
|
||||||
|
{
|
||||||
|
return getenv((ctype == CFG_DELAY ? CFG_DELAY_PATH : CFG_FAILURE_PATH));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
count_lines(const char *rs)
|
||||||
|
{
|
||||||
|
int lines = 0;
|
||||||
|
while (*(rs)++ != '\0')
|
||||||
|
if (*rs == '\n' || *rs == '\r')
|
||||||
|
lines++;
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
smash_parse_cfg(enum CFG ctype, void **cfg)
|
||||||
|
{
|
||||||
|
struct cfg_delays *delays;
|
||||||
|
struct cfg_failures *failures;
|
||||||
|
void *data;
|
||||||
|
int ret, lines;
|
||||||
|
size_t data_size, nline, n_cfg;
|
||||||
|
char *config_path, *line, err_buf[100];
|
||||||
|
const char *rs;
|
||||||
|
regex_t r;
|
||||||
|
regmatch_t rm[5];
|
||||||
|
void (*f)();
|
||||||
|
|
||||||
|
if (!(config_path = smash_get_config_path(ctype))) {
|
||||||
|
*cfg = NULL;
|
||||||
|
return ctype == CFG_DELAY ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(data = smash_load_file_in_memory(config_path, &data_size)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
lines = count_lines(data);
|
||||||
|
*cfg = malloc(ctype == CFG_DELAY ? sizeof(struct cfg_delays) +
|
||||||
|
lines * sizeof(struct cfg_delay)
|
||||||
|
: sizeof(struct cfg_failures) +
|
||||||
|
lines * sizeof(struct cfg_delay));
|
||||||
|
|
||||||
|
if (ctype == CFG_DELAY) {
|
||||||
|
delays = *cfg;
|
||||||
|
delays->size = lines;
|
||||||
|
rs = "([0-9]+);([0-9]+);([0-9]+);(-?[0-9]+)";
|
||||||
|
n_cfg = 5;
|
||||||
|
f = smash_populate_delay;
|
||||||
|
} else {
|
||||||
|
failures = *cfg;
|
||||||
|
failures->size = lines;
|
||||||
|
rs = "([0-9]+);([0-9]+)";
|
||||||
|
n_cfg = 3;
|
||||||
|
f = smash_populate_failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = regcomp(&r, rs, REG_EXTENDED)) != 0) {
|
||||||
|
regerror(ret, &r, err_buf, 100);
|
||||||
|
fprintf(stderr, "failed to compile regex <%s>:%s\n", rs,
|
||||||
|
err_buf);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
nline = 0;
|
||||||
|
line = strtok(data, "\n");
|
||||||
|
while (line) {
|
||||||
|
/* if line is a comment or snaphot, do smth */
|
||||||
|
if ((ret = regexec(&r, line, n_cfg, rm, 0)) != 0) {
|
||||||
|
regerror(ret, &r, err_buf, 100);
|
||||||
|
fprintf(stderr, "line %ld: %s\n", nline + 1, err_buf);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
f(line, nline, rm, *cfg);
|
||||||
|
nline++;
|
||||||
|
line = strtok(NULL, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
regfree(&r);
|
||||||
|
munmap(data, data_size);
|
||||||
|
return 0;
|
||||||
|
err:
|
||||||
|
regfree(&r);
|
||||||
|
munmap(data, data_size);
|
||||||
|
return -1;
|
||||||
|
}
|
49
parser.h
Normal file
49
parser.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#ifndef PARSER_H
|
||||||
|
#define PARSER_H
|
||||||
|
|
||||||
|
#define CFG_DELAY_PATH "SMASH_MPI_DELAY"
|
||||||
|
#define CFG_FAILURE_PATH "SMASH_MPI_FAILURE"
|
||||||
|
|
||||||
|
enum CFG { CFG_DELAY, CFG_FAILURE };
|
||||||
|
|
||||||
|
int smash_parse_cfg(enum CFG ctype, void **data);
|
||||||
|
|
||||||
|
struct cfg_delay {
|
||||||
|
unsigned long int delay;
|
||||||
|
unsigned int src, dst;
|
||||||
|
int msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cfg_failure {
|
||||||
|
unsigned long int time;
|
||||||
|
unsigned int node;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Structure tail padding optimization
|
||||||
|
* with flexible array member, valid in C99.
|
||||||
|
*
|
||||||
|
* To allocate do:
|
||||||
|
*
|
||||||
|
* struct cfg_delays *delays;
|
||||||
|
*
|
||||||
|
* delays = malloc(sizeof(struct cfg_delays) + VECTOR_SIZE * sizeof(config_delay));
|
||||||
|
* delays->size = VECTOR_SIZE;
|
||||||
|
*
|
||||||
|
* and to free everything do:
|
||||||
|
* free(delays)
|
||||||
|
*
|
||||||
|
* Where VECTOR_SIZE is the number of lines in one config file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct cfg_delays {
|
||||||
|
unsigned int size;
|
||||||
|
struct cfg_delay delays[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cfg_failures {
|
||||||
|
unsigned int size;
|
||||||
|
struct cfg_failure failures[];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* PARSER_H */
|
Loading…
Reference in New Issue
Block a user