diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c1b447..365bc90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -342,6 +342,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING) add_executable(driver_perf TestDriver.cpp) target_compile_definitions(driver_perf PRIVATE PERF_TEST=1) target_link_libraries(driver_perf PRIVATE ${PROJECT_NAME}) + + # server bench + add_executable(server_bench ServerBench.cpp) + target_link_libraries(server_bench PRIVATE ${PROJECT_NAME}) + set_target_properties(server_bench PROPERTIES SKIP_BUILD_RPATH ON) endif() # packaging diff --git a/ServerBench.cpp b/ServerBench.cpp new file mode 100644 index 0000000..7f46398 --- /dev/null +++ b/ServerBench.cpp @@ -0,0 +1,177 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ConflictSet.h" +#include "third_party/nadeau.h" + +void workload(weaselab::ConflictSet *cs) { + int64_t version = 0; + constexpr int kWindowSize = 45e6; + for (;;) { + uint8_t buf[sizeof(version)]; + int64_t be = __builtin_bswap64(version); + memcpy(buf, &be, sizeof(be)); + weaselab::ConflictSet::WriteRange w; + w.begin.p = buf; + w.begin.len = sizeof(buf); + w.end.len = 0; + cs->addWrites(&w, 1, version); + ++version; + if (version - kWindowSize >= 0) { + cs->setOldestVersion(version - kWindowSize); + } + } +} + +// Adapted from getaddrinfo man page +int getListenFd(const char *node, const char *service) { + + struct addrinfo hints; + struct addrinfo *result, *rp; + int sfd, s; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* stream socket */ + hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = nullptr; + hints.ai_addr = nullptr; + hints.ai_next = nullptr; + + s = getaddrinfo(node, service, &hints, &result); + if (s != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); + abort(); + } + + /* getaddrinfo() returns a list of address structures. + Try each address until we successfully bind(2). + If socket(2) (or bind(2)) fails, we (close the socket + and) try the next address. */ + + for (rp = result; rp != nullptr; rp = rp->ai_next) { + sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sfd == -1) { + continue; + } + + int val = 1; + setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + + if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) { + break; /* Success */ + } + + close(sfd); + } + + freeaddrinfo(result); /* No longer needed */ + + if (rp == nullptr) { /* No address succeeded */ + fprintf(stderr, "Could not bind\n"); + abort(); + } + + int rv = listen(sfd, SOMAXCONN); + if (rv) { + perror("listen()"); + abort(); + } + + return sfd; +} + +// HTTP response +// +std::string_view part1 = + "HTTP/1.1 200 OK \r\nContent-type: text/plain; version=0.0.4; " + "charset=utf-8; escaping=values\r\nContent-Length: "; +// Decimal content length +std::string_view part2 = "\r\n\r\n"; +// Body + +double toSeconds(timeval t) { + return double(t.tv_sec) + double(t.tv_usec) * 1e-6; +} + +int main(int argc, char **argv) { + if (argc != 3) { + goto fail; + } + { + int listenFd = getListenFd(argv[1], argv[2]); + + weaselab::ConflictSet cs{0}; + weaselab::ConflictSet::MetricsV1 *metrics; + int metricsCount; + cs.getMetricsV1(&metrics, &metricsCount); + + auto w = std::thread{workload, &cs}; + + for (;;) { + struct sockaddr_storage peer_addr = {}; + socklen_t peer_addr_len = sizeof(peer_addr); + const int connfd = + accept(listenFd, (struct sockaddr *)&peer_addr, &peer_addr_len); + + std::string body; + + rusage r; + getrusage(RUSAGE_SELF, &r); + body += "# HELP process_cpu_seconds_total Total user and system CPU time " + "spent in seconds.\n# TYPE process_cpu_seconds_total counter\n" + "process_cpu_seconds_total "; + body += std::to_string(toSeconds(r.ru_utime) + toSeconds(r.ru_stime)); + body += "\n"; + body += "# HELP process_resident_memory_bytes Resident memory size in " + "bytes.\n# TYPE process_resident_memory_bytes gauge\n" + "process_resident_memory_bytes "; + body += std::to_string(getCurrentRSS()); + body += "\n"; + + for (int i = 0; i < metricsCount; ++i) { + body += "# HELP "; + body += metrics[i].name; + body += " "; + body += metrics[i].help; + body += "\n"; + body += "# TYPE "; + body += metrics[i].name; + body += " "; + body += metrics[i].type == metrics[i].Counter ? "counter" : "gauge"; + body += "\n"; + body += metrics[i].name; + body += " "; + body += std::to_string(metrics[i].getValue()); + body += "\n"; + } + + auto len = std::to_string(body.size()); + iovec iov[] = { + {(void *)part1.data(), part1.size()}, + {(void *)len.data(), len.size()}, + {(void *)part2.data(), part2.size()}, + {(void *)body.data(), body.size()}, + }; + int written; + do { + written = writev(connfd, iov, sizeof(iov) / sizeof(iov[0])); + } while (written < 0 && errno == EINTR); + close(connfd); + } + } +fail: + fprintf(stderr, "Expected ./%s \n", argv[0]); + return 1; +} \ No newline at end of file diff --git a/third_party/nadeau.h b/third_party/nadeau.h new file mode 100644 index 0000000..fe135ad --- /dev/null +++ b/third_party/nadeau.h @@ -0,0 +1,118 @@ +/* + * Author: David Robert Nadeau + * Site: http://NadeauSoftware.com/ + * License: Creative Commons Attribution 3.0 Unported License + * http://creativecommons.org/licenses/by/3.0/deed.en_US + */ + +#if defined(_WIN32) +#include +#include + +#elif defined(__unix__) || defined(__unix) || defined(unix) || \ + (defined(__APPLE__) && defined(__MACH__)) +#include +#include + +#if defined(__APPLE__) && defined(__MACH__) +#include + +#elif (defined(_AIX) || defined(__TOS__AIX__)) || \ + (defined(__sun__) || defined(__sun) || \ + defined(sun) && (defined(__SVR4) || defined(__svr4__))) +#include +#include + +#elif defined(__linux__) || defined(__linux) || defined(linux) || \ + defined(__gnu_linux__) +#include + +#endif + +#else +#error "Cannot define getPeakRSS( ) or getCurrentRSS( ) for an unknown OS." +#endif + +/** + * Returns the peak (maximum so far) resident set size (physical + * memory use) measured in bytes, or zero if the value cannot be + * determined on this OS. + */ +inline size_t getPeakRSS() { +#if defined(_WIN32) + /* Windows -------------------------------------------------- */ + PROCESS_MEMORY_COUNTERS info; + GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); + return (size_t)info.PeakWorkingSetSize; + +#elif (defined(_AIX) || defined(__TOS__AIX__)) || \ + (defined(__sun__) || defined(__sun) || \ + defined(sun) && (defined(__SVR4) || defined(__svr4__))) + /* AIX and Solaris ------------------------------------------ */ + struct psinfo psinfo; + int fd = -1; + if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1) + return (size_t)0L; /* Can't open? */ + if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) { + close(fd); + return (size_t)0L; /* Can't read? */ + } + close(fd); + return (size_t)(psinfo.pr_rssize * 1024L); + +#elif defined(__unix__) || defined(__unix) || defined(unix) || \ + (defined(__APPLE__) && defined(__MACH__)) + /* BSD, Linux, and OSX -------------------------------------- */ + struct rusage rusage; + getrusage(RUSAGE_SELF, &rusage); +#if defined(__APPLE__) && defined(__MACH__) + return (size_t)rusage.ru_maxrss; +#else + return (size_t)(rusage.ru_maxrss * 1024L); +#endif + +#else + /* Unknown OS ----------------------------------------------- */ + return (size_t)0L; /* Unsupported. */ +#endif +} + +/** + * Returns the current resident set size (physical memory use) measured + * in bytes, or zero if the value cannot be determined on this OS. + */ +inline size_t getCurrentRSS() { +#if defined(_WIN32) + /* Windows -------------------------------------------------- */ + PROCESS_MEMORY_COUNTERS info; + GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); + return (size_t)info.WorkingSetSize; + +#elif defined(__APPLE__) && defined(__MACH__) + /* OSX ------------------------------------------------------ */ + struct mach_task_basic_info info; + mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; + if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, + &infoCount) != KERN_SUCCESS) + return (size_t)0L; /* Can't access? */ + return (size_t)info.resident_size; + +#elif defined(__linux__) || defined(__linux) || defined(linux) || \ + defined(__gnu_linux__) + /* Linux ---------------------------------------------------- */ + long rss = 0L; + FILE *fp = NULL; + if ((fp = fopen("/proc/self/statm", "r")) == NULL) + return (size_t)0L; /* Can't open? */ + if (fscanf(fp, "%*s%ld", &rss) != 1) { + fclose(fp); + return (size_t)0L; /* Can't read? */ + } + fclose(fp); + return (size_t)rss * (size_t)sysconf(_SC_PAGESIZE); + +#else + /* AIX, BSD, Solaris, and Unknown OS ------------------------ */ + return (size_t)0L; /* Unsupported. */ +#endif +} \ No newline at end of file