citrun

watch C/C++ source code execute
Log | Files | Refs | LICENSE

commit b5b211e659abe01734b294a39b6ceeb4786bd77d
parent 3118708119a43d9dd745e8495d8467c368ca5a39
Author: Kyle Milz <krwmilz@gmail.com>
Date:   Sun, 25 Mar 2018 11:00:23 -0700

lib: rename files mostly removing lib_ prefix

Diffstat:
MJamrules | 4++--
Mbin/gl_runtime.cc | 2+-
Mbin/inst_action.cc | 4++--
Mbin/inst_fe.cc | 2+-
Mlib/Jamfile | 6+++---
Alib/citrun.c | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rlib/lib.h -> lib/citrun.h | 0
Dlib/lib.c | 137-------------------------------------------------------------------------------
Dlib/lib_unix.c | 182-------------------------------------------------------------------------------
Dlib/lib_win32.c | 151------------------------------------------------------------------------------
Rlib/lib_os.h -> lib/os.h | 0
Alib/unix.c | 182+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/win32.c | 151++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mt/mem.pm | 4++--
14 files changed, 481 insertions(+), 481 deletions(-)

diff --git a/Jamrules b/Jamrules @@ -137,7 +137,7 @@ actions CCItRunRun -e "s,citrun_node_add,ccitrunrun_node_add," \ -e "s,citrun_major,ccitrunrun_major,g" \ -e "s,citrun_minor,ccitrunrun_minor,g" \ - lib.h lib.c inst_action.cc inst_fe.cc gl_procfile.cc + citrun.h citrun.c inst_action.cc inst_fe.cc gl_procfile.cc # # Change binary names so we can do a side by side installation. @@ -147,7 +147,7 @@ actions CCItRunRun -e "s,citrun_term,ccitrunrun_term," \ -e "s,citrun_gl,ccitrunrun_gl," \ -e "s,libcitrun,libccitrunrun," \ - Jamfile lib.c inst_main.cc + Jamfile citrun.c inst_main.cc jam } diff --git a/bin/gl_runtime.cc b/bin/gl_runtime.cc @@ -24,7 +24,7 @@ #include <iostream> // std::cerr #include <sstream> // std::stringstream -#include "lib.h" // struct citrun_node +#include "citrun.h" // struct citrun_node #include "gl_runtime.h" // citrun::gl_transunit, citrun::gl_procfile diff --git a/bin/inst_action.cc b/bin/inst_action.cc @@ -14,7 +14,7 @@ // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // #include "inst_action.h" -#include "lib_h.h" +#include "citrun_h.h" // citrun_h #include <clang/Frontend/CompilerInstance.h> #include <fstream> @@ -73,7 +73,7 @@ R"(#ifdef __cplusplus extern "C" { #endif )"; - preamble << lib_h; + preamble << citrun_h; preamble << "static struct citrun_node _citrun = {\n" << " " << num_lines << ",\n" << " \"" << m_compiler_file_name << "\",\n" diff --git a/bin/inst_fe.cc b/bin/inst_fe.cc @@ -15,7 +15,7 @@ // #include "inst_action.h" // InstrumentActionFactory #include "inst_fe.h" -#include "lib.h" // citrun_major, citrun_minor +#include "citrun.h" // citrun_major, citrun_minor #include <clang/Basic/Diagnostic.h> // IgnoringDiagConsumer #include <clang/Tooling/CommonOptionsParser.h> diff --git a/lib/Jamfile b/lib/Jamfile @@ -1,8 +1,8 @@ SubDir TOP lib ; -Stringize lib_h.h : lib.h ; +Stringize citrun_h.h : citrun.h ; -ObjectCcFlags lib.c lib_unix.c : -fPIC -ansi ; -Library libcitrun : lib.c lib_unix.c ; +ObjectCcFlags citrun.c unix.c : -fPIC -ansi ; +Library libcitrun : citrun.c unix.c ; InstallLib $(PREFIX)/lib : libcitrun.a ; diff --git a/lib/citrun.c b/lib/citrun.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2016 Kyle Milz <kyle@0x30.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <stdlib.h> /* exit */ +#include <stdio.h> /* fprintf, stderr */ +#include <string.h> /* strncpy */ + +#include "citrun.h" /* struct citrun_header, struct citrun_node */ +#include "os.h" /* citrun_{extend,os_info,open_fd} */ + + +/* + * The purpose of the instrumentation runtime is to create a file in a well + * known location and constantly write counter information to it. + * + * Once the file is initially created it does not change in size. There is one + * `struct citrun_node` per instrumented translation unit. The file is + * structured like the following: + * + * /tmp/citrun/a.out_XXXXXXX: + * + * +-----------------------+ + * | struct citrun_header | + * | - citrun version | + * | - process ids | + * | - program name | + * | - working directory | + * | padding | + * +-----------------------+ + * | struct citrun_node 1 | + * | - source file paths | + * | - counter buffer | <-- size ~ lines in source file + * | padding | + * +-----------------------+ + * | struct citrun_node .. | + * | - source file paths | + * | - counter buffer | + * | padding | + * + ----------------------+------ + * | struct citrun_node N | ^ + * | - source file paths | |-- sized for efficient memory access + * | - counter buffer | | + * | padding | v + * +-----------------------+------ + */ + +/* + * Internal function that extends the file descriptor given as the first + * argument and fills the extended space with version and runtime information. + * + * Returns a pointer to the beginning of the extended region on success. + * The instrumented program will exit nonzero on failure. + */ +static struct citrun_header * +citrun_add_header(int fd) +{ + struct citrun_header *new_header; + + new_header = citrun_extend(fd, sizeof(struct citrun_header)); + + strncpy(new_header->magic, "ctrn", sizeof(new_header->magic)); + new_header->major = citrun_major; + new_header->minor = citrun_minor; + + /* Fill in various runtime information fields. */ + citrun_os_info(new_header); + + return new_header; +} + +/* + * Public function called by code inserted into each translation unit. Takes a + * version major and minor as the first two arguments and a pointer to an + * existing `struct citrun_node` as the third argument. + * + * If the passed version numbers don't exactly match the version numbers this + * library was compiled with a runtime error occurs. This means all instrumented + * object files must be generated by the same citrun tools. + * + * The passed in `struct citrun_node` gets its fields copied to the runtime + * file, and extra counter buffer space is allocated. + * + * Instrumented program will exit nonzero on failure. + */ +void +citrun_node_add(unsigned int major, unsigned int minor, struct citrun_node *n) +{ + size_t sz; + struct citrun_node *new; + static struct citrun_header *header = NULL; + static int fd = 0; + + /* Binary compatibility between versions not guaranteed. */ + if (major != citrun_major || minor != citrun_minor) { + fprintf(stderr, "libcitrun %i.%i: incompatible version %i.%i.\n" + "Try cleaning and rebuilding your project.\n", + citrun_major, citrun_minor, major, minor); + exit(1); + } + + if (header == NULL) { + fd = citrun_open_fd(); + header = citrun_add_header(fd); + citrun_start_viewer(); + } + + /* Allocate enough room for node and live execution buffers. */ + sz = sizeof(struct citrun_node); + sz += n->size * sizeof(unsigned long long); + new = citrun_extend(fd, sz); + + /* Increment accumulation fields in header. */ + header->units++; + header->loc += n->size; + + /* Copy these fields from incoming node verbatim. */ + new->size = n->size; + strncpy(new->comp_file_path, n->comp_file_path, CITRUN_PATH_MAX); + strncpy(new->abs_file_path, n->abs_file_path, CITRUN_PATH_MAX); + new->comp_file_path[CITRUN_PATH_MAX - 1] = '\0'; + new->abs_file_path[CITRUN_PATH_MAX - 1] = '\0'; + + /* Set incoming nodes data pointer to allocated space after struct. */ + n->data = (unsigned long long *)(new + 1); +} diff --git a/lib/lib.h b/lib/citrun.h diff --git a/lib/lib.c b/lib/lib.c @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2016 Kyle Milz <kyle@0x30.net> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#include <stdlib.h> /* exit */ -#include <stdio.h> /* fprintf, stderr */ -#include <string.h> /* strncpy */ - -#include "lib.h" /* struct citrun_header, struct citrun_node */ -#include "lib_os.h" /* citrun_{extend,os_info,open_fd} */ - - -/* - * The purpose of the instrumentation runtime is to create a file in a well - * known location and constantly write counter information to it. - * - * Once the file is initially created it does not change in size. There is one - * `struct citrun_node` per instrumented translation unit. The file is - * structured like the following: - * - * /tmp/citrun/a.out_XXXXXXX: - * - * +-----------------------+ - * | struct citrun_header | - * | - citrun version | - * | - process ids | - * | - program name | - * | - working directory | - * | padding | - * +-----------------------+ - * | struct citrun_node 1 | - * | - source file paths | - * | - counter buffer | <-- size ~ lines in source file - * | padding | - * +-----------------------+ - * | struct citrun_node .. | - * | - source file paths | - * | - counter buffer | - * | padding | - * + ----------------------+------ - * | struct citrun_node N | ^ - * | - source file paths | |-- sized for efficient memory access - * | - counter buffer | | - * | padding | v - * +-----------------------+------ - */ - -/* - * Internal function that extends the file descriptor given as the first - * argument and fills the extended space with version and runtime information. - * - * Returns a pointer to the beginning of the extended region on success. - * The instrumented program will exit nonzero on failure. - */ -static struct citrun_header * -citrun_add_header(int fd) -{ - struct citrun_header *new_header; - - new_header = citrun_extend(fd, sizeof(struct citrun_header)); - - strncpy(new_header->magic, "ctrn", sizeof(new_header->magic)); - new_header->major = citrun_major; - new_header->minor = citrun_minor; - - /* Fill in various runtime information fields. */ - citrun_os_info(new_header); - - return new_header; -} - -/* - * Public function called by code inserted into each translation unit. Takes a - * version major and minor as the first two arguments and a pointer to an - * existing `struct citrun_node` as the third argument. - * - * If the passed version numbers don't exactly match the version numbers this - * library was compiled with a runtime error occurs. This means all instrumented - * object files must be generated by the same citrun tools. - * - * The passed in `struct citrun_node` gets its fields copied to the runtime - * file, and extra counter buffer space is allocated. - * - * Instrumented program will exit nonzero on failure. - */ -void -citrun_node_add(unsigned int major, unsigned int minor, struct citrun_node *n) -{ - size_t sz; - struct citrun_node *new; - static struct citrun_header *header = NULL; - static int fd = 0; - - /* Binary compatibility between versions not guaranteed. */ - if (major != citrun_major || minor != citrun_minor) { - fprintf(stderr, "libcitrun %i.%i: incompatible version %i.%i.\n" - "Try cleaning and rebuilding your project.\n", - citrun_major, citrun_minor, major, minor); - exit(1); - } - - if (header == NULL) { - fd = citrun_open_fd(); - header = citrun_add_header(fd); - citrun_start_viewer(); - } - - /* Allocate enough room for node and live execution buffers. */ - sz = sizeof(struct citrun_node); - sz += n->size * sizeof(unsigned long long); - new = citrun_extend(fd, sz); - - /* Increment accumulation fields in header. */ - header->units++; - header->loc += n->size; - - /* Copy these fields from incoming node verbatim. */ - new->size = n->size; - strncpy(new->comp_file_path, n->comp_file_path, CITRUN_PATH_MAX); - strncpy(new->abs_file_path, n->abs_file_path, CITRUN_PATH_MAX); - new->comp_file_path[CITRUN_PATH_MAX - 1] = '\0'; - new->abs_file_path[CITRUN_PATH_MAX - 1] = '\0'; - - /* Set incoming nodes data pointer to allocated space after struct. */ - n->data = (unsigned long long *)(new + 1); -} diff --git a/lib/lib_unix.c b/lib/lib_unix.c @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2016 Kyle Milz <kyle@0x30.net> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#include <sys/mman.h> /* mmap */ -#include <sys/stat.h> /* S_IRUSR, S_IWUSR, mkdir */ - -#include <assert.h> -#include <err.h> -#include <errno.h> /* EEXIST */ -#include <fcntl.h> /* O_CREAT */ -#include <limits.h> /* PATH_MAX */ -#include <stdlib.h> /* get{env,progname} */ -#include <string.h> /* strl{cpy,cat} */ -#include <unistd.h> /* access, execlp, fork, lseek, get* */ - -#include "lib.h" /* struct citrun_header */ -#include "lib_os.h" - -#define UNIX_PROCDIR "/tmp/citrun" - - -/* - * Implementation of lib_os.h interface for at least: - * - OpenBSD - * - Darwin - * - Linux - */ - -/* - * Rounds up the second argument to a multiple of the system page size, which - * makes working with mmap nicer. - * - * Get the current mapping length, extend it by truncation and then extend the - * memory mapping. - * - * If this function fails the instrumented program will exit nonzero. - */ -void * -citrun_extend(int fd, size_t req_bytes) -{ - size_t aligned_bytes; - off_t len; - void *mem; - size_t page_mask; - - page_mask = getpagesize() - 1; - aligned_bytes = (req_bytes + page_mask) & ~page_mask; - - /* Get current file length. */ - if ((len = lseek(fd, 0, SEEK_END)) < 0) - err(1, "lseek"); - - /* Increase file length, filling with zeros. */ - if (ftruncate(fd, len + aligned_bytes) < 0) - err(1, "ftruncate from %lld to %llu", len, len + aligned_bytes); - - /* Increase memory mapping length. */ - mem = mmap(NULL, req_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, len); - - if (mem == MAP_FAILED) - err(1, "mmap %zu bytes @ %llu", req_bytes, len); - - return mem; -} - -/* - * If CITRUN_PROCDIR is not set UNIX_PROCDIR is used as a prefix. Attempt to - * create the prefix but don't error if it already exists. - * - * Create a file name by concatenating the program name with a 10 character (man - * page suggests this amount) random template and pass that to mkstemp(3). - * - * If this program fails the instrumented program will exit nonzero. - */ -int -citrun_open_fd() -{ - const char *procdir; - char procfile[PATH_MAX]; - int fd; - - if ((procdir = getenv("CITRUN_PROCDIR")) == NULL) - procdir = UNIX_PROCDIR; - - if (mkdir(procdir, S_IRWXU) && errno != EEXIST) - err(1, "mkdir '%s'", procdir); - - strlcpy(procfile, procdir, PATH_MAX); - strlcat(procfile, "/", PATH_MAX); - strlcat(procfile, getprogname(), PATH_MAX); - strlcat(procfile, "_XXXXXXXXXX", PATH_MAX); - - if ((fd = mkstemp(procfile)) < 0) - err(1, "mkstemp"); - - return fd; -} - -/* - * Fills in the following fields: - * - process id - * - parent process id - * - process group - * - program name - * - current working directory - * - * This function doesn't fail. - */ -void -citrun_os_info(struct citrun_header *h) -{ - h->pids[0] = getpid(); - h->pids[1] = getppid(); - h->pids[2] = getpgrp(); - - strlcpy(h->progname, getprogname(), sizeof(h->progname)); - - if (getcwd(h->cwd, sizeof(h->cwd)) == NULL) - strncpy(h->cwd, "", sizeof(h->cwd)); -} - -/* - * Checks for the global citrun_gl lock file in the directory either given by the - * CITRUN_PROCDIR environment variable value or UNIX_PROCDIR if CITRUN_PROCDIR - * doesn't exist. - * - * If no lock file exists a new process is forked that tries to exec 'citrun_gl' - * which must be on the path. - * - * The instrumented program exits on failure and returns nothing on success. - */ -void -citrun_start_viewer() -{ - pid_t pid; - const char *procdir; - char viewer_file[PATH_MAX]; - - if ((procdir = getenv("CITRUN_PROCDIR")) == NULL) - procdir = UNIX_PROCDIR; - - strlcpy(viewer_file, procdir, PATH_MAX); - strlcat(viewer_file, "/", PATH_MAX); - strlcat(viewer_file, "citrun_gl.lock", PATH_MAX); - - if (access(viewer_file, F_OK)) { - /* If errno was ENOENT then fall through otherwise error. */ - if (errno != ENOENT) - err(1, "access"); - } else - /* File already exists, don't create a new viewer. */ - return; - - pid = fork(); - if (pid < 0) - err(1, "fork"); - else if (pid > 0) - /* In parent process. */ - return; - - /* - * Use a different name than the instrumented program this library is - * linked to for better diagnostics in error messages. - */ - setprogname("libcitrun"); - - /* In child process, exec the viewer. */ - if (execlp("citrun_gl", "citrun_gl", NULL)) - err(1, "exec citrun_gl"); -} diff --git a/lib/lib_win32.c b/lib/lib_win32.c @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2016 Kyle Milz <kyle@0x30.net> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#include <direct.h> /* getcwd */ -#include <stdio.h> /* stderr */ -#include <windows.h> /* HANDLE, MapViewOfFile, ... */ -#include <io.h> -#define PATH_MAX 32000 - -#include "lib.h" /* struct citrun_header */ -#include "lib_os.h" - - -static HANDLE h = INVALID_HANDLE_VALUE; - -static void -Err(int code, const char *fmt) -{ - char buf[256]; - - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, NULL); - - fprintf(stderr, "%s: %s", fmt, buf); - exit(code); -} - -static HANDLE -mkstemp(char *template) -{ - int i; - unsigned int r; - - char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - - for (i = strlen(template) - 1; i > 0; --i) { - if (template[i] != 'X') - break; - - if (rand_s(&r)) { - fprintf(stderr, "rand failed: %s\n", strerror(errno)); - exit(1); - } - - template[i] = chars[r % (sizeof(chars) - 1)]; - } - - return CreateFile( - template, - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - CREATE_NEW, - 0, - NULL - ); -} - -/* - * Extends the file and memory mapping length of fd by a requested amount of - * bytes (rounded up to the next page size). - * Returns a pointer to the extended region on success, exits on failure. - */ -void * -citrun_extend(size_t req_bytes) -{ - size_t aligned_bytes; - size_t len; - HANDLE fm; - void *mem; - size_t page_mask; - - SYSTEM_INFO system_info; - GetSystemInfo(&system_info); - - page_mask = system_info.dwAllocationGranularity - 1; - aligned_bytes = (req_bytes + page_mask) & ~page_mask; - - /* Get current file length. */ - if ((len = GetFileSize(h, NULL)) == INVALID_FILE_SIZE) - Err(1, "GetFileSize"); - - /* Increase file pointer to new length. */ - if (SetFilePointer(h, len + aligned_bytes, NULL, FILE_BEGIN) - == INVALID_SET_FILE_POINTER) - Err(1, "SetFilePointer"); - - /* Set new length. */ - if (SetEndOfFile(h) == 0) - Err(1, "SetEndOfFile"); - - /* Create a new mapping that's used temporarily. */ - if ((fm = CreateFileMapping(h, NULL, PAGE_READWRITE, 0, 0, NULL)) == NULL) - Err(1, "CreateFileMapping"); - - /* Create a new memory mapping for the newly extended space. */ - if ((mem = MapViewOfFile(fm, FILE_MAP_READ | FILE_MAP_WRITE, 0, len, req_bytes)) == NULL) - Err(1, "MapViewOfFile"); - - CloseHandle(fm); - return mem; -} - -/* - * Opens a file with a random suffix. Exits on error. - */ -void -citrun_open_fd() -{ - char *procdir; - char procfile[PATH_MAX]; - - if ((procdir = getenv("CITRUN_PROCDIR")) == NULL) - procdir = "C:\\CItRun"; - - if (CreateDirectory(procdir, NULL) == 0 && - GetLastError() != ERROR_ALREADY_EXISTS) - Err(1, "CreateDirectory"); - - strncpy(procfile, procdir, PATH_MAX); - strncat(procfile, "\\", PATH_MAX); - strncat(procfile, "program", PATH_MAX); - strncat(procfile, "_XXXXXXXXXX", PATH_MAX); - - if ((h = mkstemp(procfile)) == INVALID_HANDLE_VALUE) - Err(1, "mkstemp"); -} - -void -citrun_os_info(struct citrun_header *h) -{ - h->pids[0] = getpid(); - - if (GetModuleFileName(NULL, h->progname, sizeof(h->progname)) == 0) - Err(1, "GetModuleFileName"); - - if (getcwd(h->cwd, sizeof(h->cwd)) == NULL) - strncpy(h->cwd, "", sizeof(h->cwd)); -} diff --git a/lib/lib_os.h b/lib/os.h diff --git a/lib/unix.c b/lib/unix.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2016 Kyle Milz <kyle@0x30.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <sys/mman.h> /* mmap */ +#include <sys/stat.h> /* S_IRUSR, S_IWUSR, mkdir */ + +#include <assert.h> +#include <err.h> +#include <errno.h> /* EEXIST */ +#include <fcntl.h> /* O_CREAT */ +#include <limits.h> /* PATH_MAX */ +#include <stdlib.h> /* get{env,progname} */ +#include <string.h> /* strl{cpy,cat} */ +#include <unistd.h> /* access, execlp, fork, lseek, get* */ + +#include "citrun.h" /* struct citrun_header */ +#include "os.h" + +#define UNIX_PROCDIR "/tmp/citrun" + + +/* + * Implementation of os.h interface for at least: + * - OpenBSD + * - Darwin + * - Linux + */ + +/* + * Rounds up the second argument to a multiple of the system page size, which + * makes working with mmap nicer. + * + * Get the current mapping length, extend it by truncation and then extend the + * memory mapping. + * + * If this function fails the instrumented program will exit nonzero. + */ +void * +citrun_extend(int fd, size_t req_bytes) +{ + size_t aligned_bytes; + off_t len; + void *mem; + size_t page_mask; + + page_mask = getpagesize() - 1; + aligned_bytes = (req_bytes + page_mask) & ~page_mask; + + /* Get current file length. */ + if ((len = lseek(fd, 0, SEEK_END)) < 0) + err(1, "lseek"); + + /* Increase file length, filling with zeros. */ + if (ftruncate(fd, len + aligned_bytes) < 0) + err(1, "ftruncate from %lld to %llu", len, len + aligned_bytes); + + /* Increase memory mapping length. */ + mem = mmap(NULL, req_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, len); + + if (mem == MAP_FAILED) + err(1, "mmap %zu bytes @ %llu", req_bytes, len); + + return mem; +} + +/* + * If CITRUN_PROCDIR is not set UNIX_PROCDIR is used as a prefix. Attempt to + * create the prefix but don't error if it already exists. + * + * Create a file name by concatenating the program name with a 10 character (man + * page suggests this amount) random template and pass that to mkstemp(3). + * + * If this program fails the instrumented program will exit nonzero. + */ +int +citrun_open_fd() +{ + const char *procdir; + char procfile[PATH_MAX]; + int fd; + + if ((procdir = getenv("CITRUN_PROCDIR")) == NULL) + procdir = UNIX_PROCDIR; + + if (mkdir(procdir, S_IRWXU) && errno != EEXIST) + err(1, "mkdir '%s'", procdir); + + strlcpy(procfile, procdir, PATH_MAX); + strlcat(procfile, "/", PATH_MAX); + strlcat(procfile, getprogname(), PATH_MAX); + strlcat(procfile, "_XXXXXXXXXX", PATH_MAX); + + if ((fd = mkstemp(procfile)) < 0) + err(1, "mkstemp"); + + return fd; +} + +/* + * Fills in the following fields: + * - process id + * - parent process id + * - process group + * - program name + * - current working directory + * + * This function doesn't fail. + */ +void +citrun_os_info(struct citrun_header *h) +{ + h->pids[0] = getpid(); + h->pids[1] = getppid(); + h->pids[2] = getpgrp(); + + strlcpy(h->progname, getprogname(), sizeof(h->progname)); + + if (getcwd(h->cwd, sizeof(h->cwd)) == NULL) + strncpy(h->cwd, "", sizeof(h->cwd)); +} + +/* + * Checks for the global citrun_gl lock file in the directory either given by the + * CITRUN_PROCDIR environment variable value or UNIX_PROCDIR if CITRUN_PROCDIR + * doesn't exist. + * + * If no lock file exists a new process is forked that tries to exec 'citrun_gl' + * which must be on the path. + * + * The instrumented program exits on failure and returns nothing on success. + */ +void +citrun_start_viewer() +{ + pid_t pid; + const char *procdir; + char viewer_file[PATH_MAX]; + + if ((procdir = getenv("CITRUN_PROCDIR")) == NULL) + procdir = UNIX_PROCDIR; + + strlcpy(viewer_file, procdir, PATH_MAX); + strlcat(viewer_file, "/", PATH_MAX); + strlcat(viewer_file, "citrun_gl.lock", PATH_MAX); + + if (access(viewer_file, F_OK)) { + /* If errno was ENOENT then fall through otherwise error. */ + if (errno != ENOENT) + err(1, "access"); + } else + /* File already exists, don't create a new viewer. */ + return; + + pid = fork(); + if (pid < 0) + err(1, "fork"); + else if (pid > 0) + /* In parent process. */ + return; + + /* + * Use a different name than the instrumented program this library is + * linked to for better diagnostics in error messages. + */ + setprogname("libcitrun"); + + /* In child process, exec the viewer. */ + if (execlp("citrun_gl", "citrun_gl", NULL)) + err(1, "exec citrun_gl"); +} diff --git a/lib/win32.c b/lib/win32.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2016 Kyle Milz <kyle@0x30.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <direct.h> /* getcwd */ +#include <stdio.h> /* stderr */ +#include <windows.h> /* HANDLE, MapViewOfFile, ... */ +#include <io.h> +#define PATH_MAX 32000 + +#include "citrun.h" /* struct citrun_header */ +#include "os.h" + + +static HANDLE h = INVALID_HANDLE_VALUE; + +static void +Err(int code, const char *fmt) +{ + char buf[256]; + + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, NULL); + + fprintf(stderr, "%s: %s", fmt, buf); + exit(code); +} + +static HANDLE +mkstemp(char *template) +{ + int i; + unsigned int r; + + char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + + for (i = strlen(template) - 1; i > 0; --i) { + if (template[i] != 'X') + break; + + if (rand_s(&r)) { + fprintf(stderr, "rand failed: %s\n", strerror(errno)); + exit(1); + } + + template[i] = chars[r % (sizeof(chars) - 1)]; + } + + return CreateFile( + template, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + CREATE_NEW, + 0, + NULL + ); +} + +/* + * Extends the file and memory mapping length of fd by a requested amount of + * bytes (rounded up to the next page size). + * Returns a pointer to the extended region on success, exits on failure. + */ +void * +citrun_extend(size_t req_bytes) +{ + size_t aligned_bytes; + size_t len; + HANDLE fm; + void *mem; + size_t page_mask; + + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + + page_mask = system_info.dwAllocationGranularity - 1; + aligned_bytes = (req_bytes + page_mask) & ~page_mask; + + /* Get current file length. */ + if ((len = GetFileSize(h, NULL)) == INVALID_FILE_SIZE) + Err(1, "GetFileSize"); + + /* Increase file pointer to new length. */ + if (SetFilePointer(h, len + aligned_bytes, NULL, FILE_BEGIN) + == INVALID_SET_FILE_POINTER) + Err(1, "SetFilePointer"); + + /* Set new length. */ + if (SetEndOfFile(h) == 0) + Err(1, "SetEndOfFile"); + + /* Create a new mapping that's used temporarily. */ + if ((fm = CreateFileMapping(h, NULL, PAGE_READWRITE, 0, 0, NULL)) == NULL) + Err(1, "CreateFileMapping"); + + /* Create a new memory mapping for the newly extended space. */ + if ((mem = MapViewOfFile(fm, FILE_MAP_READ | FILE_MAP_WRITE, 0, len, req_bytes)) == NULL) + Err(1, "MapViewOfFile"); + + CloseHandle(fm); + return mem; +} + +/* + * Opens a file with a random suffix. Exits on error. + */ +void +citrun_open_fd() +{ + char *procdir; + char procfile[PATH_MAX]; + + if ((procdir = getenv("CITRUN_PROCDIR")) == NULL) + procdir = "C:\\CItRun"; + + if (CreateDirectory(procdir, NULL) == 0 && + GetLastError() != ERROR_ALREADY_EXISTS) + Err(1, "CreateDirectory"); + + strncpy(procfile, procdir, PATH_MAX); + strncat(procfile, "\\", PATH_MAX); + strncat(procfile, "program", PATH_MAX); + strncat(procfile, "_XXXXXXXXXX", PATH_MAX); + + if ((h = mkstemp(procfile)) == INVALID_HANDLE_VALUE) + Err(1, "mkstemp"); +} + +void +citrun_os_info(struct citrun_header *h) +{ + h->pids[0] = getpid(); + + if (GetModuleFileName(NULL, h->progname, sizeof(h->progname)) == 0) + Err(1, "GetModuleFileName"); + + if (getcwd(h->cwd, sizeof(h->cwd)) == NULL) + strncpy(h->cwd, "", sizeof(h->cwd)); +} diff --git a/t/mem.pm b/t/mem.pm @@ -32,7 +32,7 @@ sub new { my $node_start = get_aligned_size($header_size); while ($node_start < $self->{size}) { - # Struct field ordering controlled by lib.h. + # Struct field ordering controlled by citrun.h. my $data = substr($self->{mem}, $node_start, $node_fixed_size); my @struct_fields = unpack("IZ256Z256", $data); @@ -73,7 +73,7 @@ sub get_buffers { 1; __DATA__ __C__ -#include "../lib/lib.h" +#include "../lib/citrun.h" size_t citrun_header_size() { return sizeof(struct citrun_header);