/* Counts the system calls a run makes, by standing in front of libc. cc -O2 -shared -fPIC -o syscount.so tests/syscount.c -ldl LD_PRELOAD=./syscount.so ./opentracker -f conf (DYLD_INSERT_LIBRARIES on macOS) The tally goes to stderr when the process exits. Reading the source tells you which calls a path should make; this tells you which it does make, which is not always the same thing. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #ifdef __linux__ #include #endif #define COUNTER(name) static _Atomic long g_##name #define BUMP(name) atomic_fetch_add_explicit(&g_##name, 1, memory_order_relaxed) #define REPORT(name) fprintf(stderr, " %-14s %10ld\n", #name, atomic_load(&g_##name)) COUNTER(accept); COUNTER(accept4); COUNTER(read); COUNTER(write); COUNTER(close); COUNTER(fcntl); COUNTER(recvfrom); COUNTER(sendto); COUNTER(epoll_ctl); COUNTER(epoll_wait); typedef int (*fn_accept)(int, struct sockaddr *, socklen_t *); typedef int (*fn_accept4)(int, struct sockaddr *, socklen_t *, int); typedef ssize_t (*fn_read)(int, void *, size_t); typedef ssize_t (*fn_write)(int, const void *, size_t); typedef int (*fn_close)(int); typedef int (*fn_fcntl)(int, int, ...); typedef ssize_t (*fn_recvfrom)(int, void *, size_t, int, struct sockaddr *, socklen_t *); typedef ssize_t (*fn_sendto)(int, const void *, size_t, int, const struct sockaddr *, socklen_t); static fn_accept real_accept; static fn_accept4 real_accept4; static fn_read real_read; static fn_write real_write; static fn_close real_close; static fn_fcntl real_fcntl; static fn_recvfrom real_recvfrom; static fn_sendto real_sendto; /* Resolving lazily keeps the library out of the way until something is actually called, which matters because dlsym itself allocates */ #define WRAP(name) \ if (!real_##name) \ real_##name = (fn_##name)dlsym(RTLD_NEXT, #name); \ BUMP(name) int accept(int s, struct sockaddr *addr, socklen_t *len) { WRAP(accept); return real_accept(s, addr, len); } #ifdef SOCK_NONBLOCK int accept4(int s, struct sockaddr *addr, socklen_t *len, int flags) { WRAP(accept4); return real_accept4(s, addr, len, flags); } #endif ssize_t read(int fd, void *buf, size_t count) { WRAP(read); return real_read(fd, buf, count); } ssize_t write(int fd, const void *buf, size_t count) { WRAP(write); return real_write(fd, buf, count); } int close(int fd) { WRAP(close); return real_close(fd); } int fcntl(int fd, int cmd, ...) { va_list args; long arg; va_start(args, cmd); arg = va_arg(args, long); va_end(args); WRAP(fcntl); return real_fcntl(fd, cmd, arg); } ssize_t recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) { WRAP(recvfrom); return real_recvfrom(s, buf, len, flags, from, fromlen); } ssize_t sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) { WRAP(sendto); return real_sendto(s, buf, len, flags, to, tolen); } #ifdef __linux__ typedef int (*fn_epoll_ctl)(int, int, int, struct epoll_event *); typedef int (*fn_epoll_wait)(int, struct epoll_event *, int, int); static fn_epoll_ctl real_epoll_ctl; static fn_epoll_wait real_epoll_wait; int epoll_ctl(int ep, int op, int fd, struct epoll_event *ev) { WRAP(epoll_ctl); return real_epoll_ctl(ep, op, fd, ev); } int epoll_wait(int ep, struct epoll_event *events, int max, int timeout) { WRAP(epoll_wait); return real_epoll_wait(ep, events, max, timeout); } #endif __attribute__((destructor)) static void report(void) { fprintf(stderr, "\n system calls this run\n"); REPORT(accept); #ifdef SOCK_NONBLOCK REPORT(accept4); #endif REPORT(read); REPORT(write); REPORT(close); REPORT(fcntl); REPORT(recvfrom); REPORT(sendto); #ifdef __linux__ REPORT(epoll_ctl); REPORT(epoll_wait); #endif }