summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJose Fonseca <jfonseca@vmware.com>2016-05-05 13:55:29 +0100
committerJose Fonseca <jfonseca@vmware.com>2016-05-05 13:55:29 +0100
commitefc5d91d983a71e8749fe2201a96dc4ca82f4389 (patch)
tree2de4654c031e141adb528dda8aed1fe4f6e9909e /lib
parentbceafecaa6a3e606dfc31c0016cecb9e82d5cab8 (diff)
os: Move to lib.
Diffstat (limited to 'lib')
-rw-r--r--lib/CMakeLists.txt3
-rw-r--r--lib/os/CMakeLists.txt40
-rw-r--r--lib/os/os.hpp106
-rw-r--r--lib/os/os_backtrace.cpp486
-rw-r--r--lib/os/os_backtrace.hpp44
-rw-r--r--lib/os/os_binary.hpp59
-rw-r--r--lib/os/os_crtdbg.cpp92
-rw-r--r--lib/os/os_crtdbg.hpp57
-rw-r--r--lib/os/os_dl.hpp87
-rw-r--r--lib/os/os_memory.hpp78
-rw-r--r--lib/os/os_osx.mm41
-rw-r--r--lib/os/os_posix.cpp468
-rw-r--r--lib/os/os_process.hpp89
-rw-r--r--lib/os/os_string.hpp437
-rw-r--r--lib/os/os_thread.hpp539
-rw-r--r--lib/os/os_thread_test.cpp161
-rw-r--r--lib/os/os_time.hpp104
-rw-r--r--lib/os/os_version.hpp61
-rw-r--r--lib/os/os_win32.cpp405
-rw-r--r--lib/os/thread_pool.hpp113
20 files changed, 3469 insertions, 1 deletions
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 813e180a..23be7605 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -1,3 +1,4 @@
-add_subdirectory (guids)
+add_subdirectory (os)
add_subdirectory (highlight)
+add_subdirectory (guids)
add_subdirectory (image)
diff --git a/lib/os/CMakeLists.txt b/lib/os/CMakeLists.txt
new file mode 100644
index 00000000..222411e0
--- /dev/null
+++ b/lib/os/CMakeLists.txt
@@ -0,0 +1,40 @@
+include_directories (
+ ${CMAKE_SOURCE_DIR}/thirdparty
+)
+
+if (WIN32)
+ set (os os_win32.cpp)
+elseif (APPLE)
+ set (os os_posix.cpp os_osx.mm)
+else ()
+ set (os os_posix.cpp)
+endif ()
+
+add_convenience_library (os
+ ${os}
+ os_backtrace.cpp
+ os_crtdbg.cpp
+)
+
+target_link_libraries (os
+ ${LIBBACKTRACE_LIBRARIES}
+)
+if (WIN32)
+ target_link_libraries (os
+ shell32
+ )
+endif ()
+if (ANDROID)
+ target_link_libraries (os
+ log
+ )
+endif ()
+
+if (APPLE)
+ target_link_libraries (os
+ "-framework Foundation"
+ )
+endif ()
+
+add_gtest (os_thread_test os_thread_test.cpp)
+target_link_libraries (os_thread_test os)
diff --git a/lib/os/os.hpp b/lib/os/os.hpp
new file mode 100644
index 00000000..b9d3fc21
--- /dev/null
+++ b/lib/os/os.hpp
@@ -0,0 +1,106 @@
+/**************************************************************************
+ *
+ * Copyright 2010 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Simple OS abstraction layer.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef _WIN32
+#ifndef snprintf
+#define snprintf _snprintf
+#endif
+#ifndef vsnprintf
+#define vsnprintf _vsnprintf
+#endif
+#ifndef strcasecmp
+#define strcasecmp stricmp
+#endif
+#endif /* !_WIN32 */
+
+namespace os {
+
+void log(const char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 1, 2)))
+#endif
+;
+
+#if defined _WIN32 || defined __CYGWIN__
+ /* We always use .def files on windows for now */
+ #if 0
+ #define PUBLIC __declspec(dllexport)
+ #else
+ #define PUBLIC
+ #endif
+ #define PRIVATE
+#else
+ #define PUBLIC __attribute__ ((visibility("default")))
+ #define PRIVATE __attribute__ ((visibility("hidden")))
+#endif
+
+/**
+ * Exit immediately.
+ *
+ * This should be called only from the wrappers, when there is no safe way of
+ * failing gracefully.
+ */
+// coverity[+kill]
+#ifdef _MSC_VER
+__declspec(noreturn)
+#endif
+void abort(void)
+#ifdef __GNUC__
+ __attribute__((__noreturn__))
+#endif
+;
+
+void
+breakpoint(void);
+
+
+void setExceptionCallback(void (*callback)(void));
+void resetExceptionCallback(void);
+
+/**
+ * Returns a pseudo-random integer in the range 0 to RAND_MAX.
+ */
+static inline int
+random(void) {
+#ifdef _WIN32
+ return ::rand();
+#else
+ return ::random();
+#endif
+}
+
+} /* namespace os */
+
diff --git a/lib/os/os_backtrace.cpp b/lib/os/os_backtrace.cpp
new file mode 100644
index 00000000..c95692bd
--- /dev/null
+++ b/lib/os/os_backtrace.cpp
@@ -0,0 +1,486 @@
+/**************************************************************************
+ *
+ * Copyright 2013 Samsung
+ * Contributed by Eugene Velesevich
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Wrapper for platform-specific code for obtaining symbolic backtraces
+ * on Android and Linux
+ */
+
+
+#include "os_backtrace.hpp"
+
+#include <set>
+#include <vector>
+#include "os.hpp"
+
+#if defined(ANDROID)
+# include <dlfcn.h>
+#elif HAVE_BACKTRACE
+# include <stdint.h>
+# include <dlfcn.h>
+# include <unistd.h>
+# include <map>
+# include <vector>
+# include <cxxabi.h>
+# include <backtrace.h>
+#endif
+
+
+using trace::Id;
+
+namespace os {
+
+/*
+ * Pascal string (with zero terminator optionally omitted)
+ * This is a helper class for storing a set of exact strings or prefixes
+ * to match a zero-terminated string against later.
+ * Two zero-terminated pstrings compare equal iff they are the same.
+ * Otherwise, they compare equal iff one is a prefix of the other
+ * (a zero-terminated pstring cannot be a prefix)
+ */
+
+struct pstring {
+ const char* s;
+ int n;
+ pstring(const char* s, int n)
+ {
+ this->s = s;
+ this->n = n;
+ }
+ bool operator<(const pstring q2) const
+ {
+ return memcmp(s, q2.s, n < q2.n? n : q2.n) < 0;
+ }
+};
+
+
+class StringPrefixes {
+private:
+ std::set<pstring> pset;
+
+ void addPrefix(char* startbuf, int n) {
+ std::set<pstring>::iterator elem = pset.find(pstring(startbuf, n));
+ bool replace = elem != pset.end() && n < elem->n;
+ if (replace) {
+ pset.erase(elem);
+ }
+ if (replace || elem == pset.end()) {
+ pset.insert(pstring(startbuf, n));
+ }
+ }
+public:
+ StringPrefixes();
+
+ bool contain(const char* s) {
+ return pset.find(pstring(s, strlen(s) + 1)) != pset.end();
+ }
+};
+
+StringPrefixes::StringPrefixes() {
+ char *list = getenv("APITRACE_BACKTRACE");
+ if (!list)
+ return;
+ for (char *t = strdup(list); ; t = NULL) {
+ char *tok = strtok(t, " \t\r\n");
+ if (!tok)
+ break;
+ if (tok[0] == '#')
+ continue;
+ if (tok[strlen(tok) - 1] == '*')
+ addPrefix(tok, strlen(tok) - 1);
+ else
+ addPrefix(tok, strlen(tok) + 1);
+ }
+}
+
+
+bool backtrace_is_needed(const char* fname) {
+ static StringPrefixes backtraceFunctionNamePrefixes;
+ return backtraceFunctionNamePrefixes.contain(fname);
+}
+
+#if defined(ANDROID)
+
+/* The following two declarations are copied from Android sources */
+
+enum DebugTargetKind {
+ kDebugTargetUnknown = 0,
+ kDebugTargetLog,
+ kDebugTargetFile,
+};
+
+struct DebugOutputTarget {
+ DebugTargetKind which;
+
+ union {
+ struct {
+ int priority;
+ const char* tag;
+ } log;
+ struct {
+ FILE* fp;
+ } file;
+ } data;
+};
+
+#define THREAD_SELF_NAME "_Z13dvmThreadSelfv"
+#define CREATE_DEBUG_TARGET_NAME "_Z25dvmCreateFileOutputTargetP17DebugOutputTargetP7__sFILE"
+#define DUMP_BACKTRACE_NAME "_Z18dvmDumpThreadStackPK17DebugOutputTargetP6Thread"
+
+
+class DalvikBacktraceProvider {
+private:
+ bool errorOccured;
+ void* (*threadself)(void);
+ FILE* streamInMemory;
+ char* buf;
+ size_t bufSize;
+ void (*dumpBacktrace)(const DebugOutputTarget*, void*);
+ DebugOutputTarget debugTarget;
+ Id nextFrameId;
+public:
+ DalvikBacktraceProvider() {
+ nextFrameId = 0;
+ FILE* (*open_memstream_exp)(char**, size_t*);
+ void (*createDebugTarget)(DebugOutputTarget*, FILE*);
+ void* handle = dlopen("/system/lib/libdvm.so", 0);
+ errorOccured = true;
+ if (!handle) {
+ os::log("dlopen failed\n");
+ return;
+ }
+ threadself = (void* (*)())dlsym(handle, THREAD_SELF_NAME);
+ if (threadself == NULL) {
+ os::log("dlsym ThreadSelf failed\n");
+ return;
+ }
+ createDebugTarget = (void (*)(DebugOutputTarget*, FILE*))dlsym(handle, CREATE_DEBUG_TARGET_NAME);
+ if (createDebugTarget == NULL) {
+ os::log("dlsym CreateFileOutput failed\n");
+ return;
+ }
+ dumpBacktrace = (void (*)(const DebugOutputTarget*, void*))dlsym(handle, DUMP_BACKTRACE_NAME);
+ if (dumpBacktrace == NULL) {
+ os::log("dlsym DumpThreadStack failed\n");
+ return;
+ }
+ void* handle2 = dlopen("/system/lib/libcutils.so", 0);
+ if (!handle2) {
+ os::log("dlopen failed\n");
+ return;
+ }
+ open_memstream_exp = (FILE* (*)(char**, size_t*))dlsym(handle2, "open_memstream");
+ if (open_memstream_exp == NULL) {
+ os::log("dlsym open_memstream failed\n");
+ return;
+ }
+ streamInMemory = open_memstream_exp(&buf, &bufSize);
+ if (streamInMemory == NULL) {
+ os::log("open_memstream failed\n");
+ return;
+ }
+ createDebugTarget(&debugTarget, streamInMemory);
+ errorOccured = false;
+ }
+
+ inline char* getBacktrace() {
+ if (errorOccured) {
+ return NULL;
+ }
+ rewind(streamInMemory);
+ dumpBacktrace(&debugTarget, threadself());
+ fflush(streamInMemory);
+ return buf;
+ }
+/*
+ * Parse a stack frame, expecting:
+ * " at android.view.HardwareRenderer$GlRenderer.initializeEgl(HardwareRenderer.java:547)"
+ * or
+ * " at android.view.HardwareRenderer$GlRenderer.initializeEgl(Native Method)"
+ */
+ std::vector<RawStackFrame> parseBacktrace(char *rawBacktrace) {
+ std::vector<RawStackFrame> parsedBacktrace;
+ char* rawBacktrace_it = rawBacktrace;
+ while (*rawBacktrace_it != '\0') {
+ RawStackFrame stackFrame;
+ // TODO: Keep a cache of stack frames
+ stackFrame.id = nextFrameId++;
+ /* skip leading space */
+ while (*rawBacktrace_it == ' ') {
+ rawBacktrace_it++;
+ }
+ /* Skip "at " */
+ rawBacktrace_it += 3;
+ stackFrame.function = rawBacktrace_it;
+ while (*rawBacktrace_it != '(') {
+ rawBacktrace_it++;
+ }
+ *rawBacktrace_it = '\0';
+ stackFrame.filename = rawBacktrace_it + 1;
+ while (*rawBacktrace_it != ':' && *rawBacktrace_it != ')') {
+ rawBacktrace_it++;
+ }
+ if (*rawBacktrace_it == ':') {
+ const char *linenumber = rawBacktrace_it + 1;
+ *rawBacktrace_it = '\0';
+ while (*rawBacktrace_it != ')') {
+ rawBacktrace_it++;
+ }
+ *rawBacktrace_it = '\0';
+ rawBacktrace_it++;
+ stackFrame.linenumber = atoi(linenumber);
+ }
+ else {
+ stackFrame.filename = NULL;
+ while (*rawBacktrace_it != '\n') {
+ rawBacktrace_it++;
+ }
+ }
+ while (*rawBacktrace_it == '\n' || *rawBacktrace_it == ' ') {
+ rawBacktrace_it++;
+ }
+ parsedBacktrace.push_back(stackFrame); /* module */
+ }
+ return parsedBacktrace;
+ }
+};
+
+std::vector<RawStackFrame> get_backtrace() {
+ static DalvikBacktraceProvider backtraceProvider;
+ return backtraceProvider.parseBacktrace(backtraceProvider.getBacktrace());
+}
+
+void dump_backtrace() {
+ /* TODO */
+}
+
+
+#elif HAVE_BACKTRACE
+
+
+static char* format(uintptr_t num, int base, char *buf, int maxlen)
+{
+ static const char digits[] = "0123456789abcdef";
+ buf += maxlen;
+ do {
+ *--buf = digits[num % base];
+ num /= base;
+ maxlen--;
+ } while (num != 0 && maxlen != 0);
+ return buf;
+}
+
+static void dump(const char *str, int len)
+{
+ static int fd = dup(STDERR_FILENO);
+ if (write(fd, str, len) != len) {
+ // Do nothing
+ }
+}
+
+static void dumpFrame(const RawStackFrame &frame)
+{
+ char buf[sizeof(long long) * 2], *p;
+#define DUMP(string) dump(string, strlen(string))
+ DUMP(frame.module ? frame.module : "?");
+ if (frame.function) {
+ DUMP(": ");
+ DUMP(frame.function);
+ }
+ if (frame.offset >= 0) {
+ DUMP("+0x");
+ p = format((uintptr_t) frame.offset, 16, buf, sizeof(buf));
+ dump(p, buf + sizeof(buf) - p);
+ }
+ if (frame.filename) {
+ DUMP(": ");
+ DUMP(frame.filename);
+ if (frame.linenumber >= 0) {
+ DUMP(":");
+ p = format((uintptr_t) frame.linenumber, 10, buf, sizeof(buf));
+ dump(p, buf + sizeof(buf) - p);
+ }
+ }
+ DUMP("\n");
+#undef DUMP
+}
+
+
+#define BT_DEPTH 10
+
+class libbacktraceProvider {
+ struct backtrace_state *state;
+ int skipFrames;
+ Id nextFrameId;
+ std::map<uintptr_t, std::vector<RawStackFrame> > cache;
+ std::vector<RawStackFrame> *current, *current_frames;
+ RawStackFrame *current_frame;
+ bool missingDwarf;
+
+ static void bt_err_callback(void *vdata, const char *msg, int errnum)
+ {
+ libbacktraceProvider *this_ = (libbacktraceProvider*)vdata;
+ if (errnum == -1)
+ this_->missingDwarf = true;
+ else if (errnum)
+ os::log("libbacktrace: %s: %s\n", msg, strerror(errnum));
+ else
+ os::log("libbacktrace: %s\n", msg);
+ }
+
+ static int bt_countskip(void *vdata, uintptr_t pc)
+ {
+ libbacktraceProvider *this_ = (libbacktraceProvider*)vdata;
+ Dl_info info1, info2;
+ if (!dladdr((void*)bt_countskip, &info2)) {
+ os::log("dladdr failed, cannot cull stack traces\n");
+ return 1;
+ }
+ if (!dladdr((void*)pc, &info1))
+ return 1;
+ if (info1.dli_fbase != info2.dli_fbase)
+ return 1;
+ this_->skipFrames++;
+ return 0;
+ }
+
+ static int bt_full_callback(void *vdata, uintptr_t pc,
+ const char *file, int line, const char *func)
+ {
+ libbacktraceProvider *this_ = (libbacktraceProvider*)vdata;
+ RawStackFrame frame = *this_->current_frame;
+ frame.id = this_->nextFrameId++;
+ frame.filename = file;
+ frame.linenumber = line;
+ if (func)
+ frame.function = func;
+ int status;
+ if (func && (func = abi::__cxa_demangle(func, NULL, NULL, &status)))
+ frame.function = func;
+ this_->current_frames->push_back(frame);
+ return 0;
+ }
+
+ static void dl_fill(RawStackFrame *frame, uintptr_t pc)
+ {
+ Dl_info info = {0};
+ dladdr((void*)pc, &info);
+ frame->module = info.dli_fname;
+ frame->function = info.dli_sname;
+ frame->offset = info.dli_saddr ? pc - (uintptr_t)info.dli_saddr
+ : pc - (uintptr_t)info.dli_fbase;
+ }
+
+ static int bt_callback(void *vdata, uintptr_t pc)
+ {
+ libbacktraceProvider *this_ = (libbacktraceProvider*)vdata;
+ std::vector<RawStackFrame> &frames = this_->cache[pc];
+ if (!frames.size()) {
+ RawStackFrame frame;
+ dl_fill(&frame, pc);
+ this_->current_frame = &frame;
+ this_->current_frames = &frames;
+ backtrace_pcinfo(this_->state, pc, bt_full_callback, bt_err_callback, vdata);
+ if (!frames.size()) {
+ frame.id = this_->nextFrameId++;
+ frames.push_back(frame);
+ }
+ }
+ this_->current->insert(this_->current->end(), frames.begin(), frames.end());
+ return this_->current->size() >= BT_DEPTH;
+ }
+
+ static int bt_full_dump_callback(void *vdata, uintptr_t pc,
+ const char *file, int line, const char *func)
+ {
+ libbacktraceProvider *this_ = (libbacktraceProvider*)vdata;
+ RawStackFrame *frame = this_->current_frame;
+ frame->filename = file;
+ frame->linenumber = line;
+ if (func)
+ frame->function = func;
+ dumpFrame(*frame);
+ return 0;
+ }
+
+ static int bt_dump_callback(void *vdata, uintptr_t pc)
+ {
+ libbacktraceProvider *this_ = (libbacktraceProvider*)vdata;
+ RawStackFrame frame;
+ dl_fill(&frame, pc);
+ this_->current_frame = &frame;
+ this_->missingDwarf = false;
+ backtrace_pcinfo(this_->state, pc, bt_full_dump_callback, bt_err_callback, vdata);
+ if (this_->missingDwarf)
+ dumpFrame(frame);
+ return 0;
+ }
+
+public:
+ libbacktraceProvider():
+ state(backtrace_create_state(NULL, 0, bt_err_callback, NULL))
+ {
+ backtrace_simple(state, 0, bt_countskip, bt_err_callback, this);
+ }
+
+ std::vector<RawStackFrame> getParsedBacktrace()
+ {
+ std::vector<RawStackFrame> parsedBacktrace;
+ current = &parsedBacktrace;
+ backtrace_simple(state, skipFrames, bt_callback, bt_err_callback, this);
+ return parsedBacktrace;
+ }
+
+ void dumpBacktrace()
+ {
+ backtrace_simple(state, 0, bt_dump_callback, bt_err_callback, this);
+ }
+};
+
+std::vector<RawStackFrame> get_backtrace() {
+ static libbacktraceProvider backtraceProvider;
+ return backtraceProvider.getParsedBacktrace();
+}
+
+void dump_backtrace() {
+ static libbacktraceProvider backtraceProvider;
+ backtraceProvider.dumpBacktrace();
+}
+
+
+#else /* !HAVE_BACKTRACE */
+
+std::vector<RawStackFrame> get_backtrace() {
+ return std::vector<RawStackFrame>();
+}
+
+void dump_backtrace() {
+}
+
+#endif
+
+
+} /* namespace os */
diff --git a/lib/os/os_backtrace.hpp b/lib/os/os_backtrace.hpp
new file mode 100644
index 00000000..72869c26
--- /dev/null
+++ b/lib/os/os_backtrace.hpp
@@ -0,0 +1,44 @@
+/**************************************************************************
+ *
+ * Copyright 2013 Samsung
+ * Contributed by Eugene Velesevich
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#pragma once
+
+#include <vector>
+
+#include "trace_model.hpp"
+
+namespace os {
+
+using trace::RawStackFrame;
+
+
+std::vector<RawStackFrame> get_backtrace();
+bool backtrace_is_needed(const char* fname);
+
+void dump_backtrace();
+
+
+} /* namespace os */
diff --git a/lib/os/os_binary.hpp b/lib/os/os_binary.hpp
new file mode 100644
index 00000000..c3a1d6ea
--- /dev/null
+++ b/lib/os/os_binary.hpp
@@ -0,0 +1,59 @@
+/**************************************************************************
+ *
+ * Copyright 2011 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Force binary mode standard files on Windows.
+ */
+
+#pragma once
+
+
+#include <stdio.h>
+
+#ifdef _WIN32
+#include <assert.h>
+
+#include <fcntl.h>
+#include <io.h>
+#endif
+
+
+namespace os {
+
+
+void setBinaryMode(FILE *fp) {
+#ifdef _WIN32
+ fflush(fp);
+ int mode = _setmode(_fileno(fp), _O_BINARY);
+ assert(mode != -1);
+ (void)mode;
+#else
+ (void)fp;
+#endif
+}
+
+
+} /* namespace os */
+
diff --git a/lib/os/os_crtdbg.cpp b/lib/os/os_crtdbg.cpp
new file mode 100644
index 00000000..21dec6ec
--- /dev/null
+++ b/lib/os/os_crtdbg.cpp
@@ -0,0 +1,92 @@
+/**************************************************************************
+ *
+ * Copyright 2015 VMware, Inc
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include "os_crtdbg.hpp"
+
+#include <assert.h>
+#include <stdio.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <crtdbg.h>
+#endif
+
+#include "os.hpp"
+
+
+namespace os {
+
+
+#ifdef _WIN32
+
+static void
+invalidParameterHandler(const wchar_t* expression,
+ const wchar_t* function,
+ const wchar_t* file,
+ unsigned int line,
+ uintptr_t pReserved)
+{
+ fprintf(stderr, "Invalid parameter detected in function %S. File: %S Line: %d\n", function, file, line);
+ fprintf(stderr, "Expression: %S\n", expression);
+ if (IsDebuggerPresent()) {
+ os::breakpoint();
+ }
+ os::abort();
+}
+
+#endif // _WIN32
+
+
+void
+setDebugOutput(Output output)
+{
+
+#ifdef _WIN32
+ // Disable assertion failure message box
+ // http://msdn.microsoft.com/en-us/library/sas1dkb2.aspx
+ _set_error_mode(_OUT_TO_STDERR);
+#ifdef _MSC_VER
+ // Disable abort message box
+ // http://msdn.microsoft.com/en-us/library/e631wekh.aspx
+ _set_abort_behavior(0, _WRITE_ABORT_MSG);
+ // Direct debug reports to stderr
+ // https://msdn.microsoft.com/en-us/library/1y71x448.aspx
+ for (int reportType = 0; reportType < _CRT_ERRCNT; ++reportType) {
+ _CrtSetReportMode(reportType, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(reportType, _CRTDBG_FILE_STDERR);
+ }
+#endif /* _MSC_VER */
+ // Set our own invalid_parameter handler
+ // https://msdn.microsoft.com/en-us/library/a9yf33zb.aspx
+ _set_invalid_parameter_handler(invalidParameterHandler);
+#endif /* _WIN32 */
+
+
+ assert(output == OUTPUT_STDERR);
+}
+
+
+} /* namespace os */
diff --git a/lib/os/os_crtdbg.hpp b/lib/os/os_crtdbg.hpp
new file mode 100644
index 00000000..87a018ba
--- /dev/null
+++ b/lib/os/os_crtdbg.hpp
@@ -0,0 +1,57 @@
+/**************************************************************************
+ *
+ * Copyright 2015 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Abstrations to force where CRT debug output should go.
+ */
+
+#pragma once
+
+
+#include <stdio.h>
+
+#ifdef _WIN32
+#include <assert.h>
+
+#include <fcntl.h>
+#include <io.h>
+#endif
+
+
+namespace os {
+
+
+enum Output {
+ OUTPUT_STDERR,
+ OUTPUT_OUTDBG
+};
+
+
+void
+setDebugOutput(Output output);
+
+
+} /* namespace os */
+
diff --git a/lib/os/os_dl.hpp b/lib/os/os_dl.hpp
new file mode 100644
index 00000000..bf79d507
--- /dev/null
+++ b/lib/os/os_dl.hpp
@@ -0,0 +1,87 @@
+/**************************************************************************
+ *
+ * Copyright 2012 Jose Fonseca
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Dynamic library linking abstraction.
+ */
+
+#pragma once
+
+
+#if defined(_WIN32)
+#include <windows.h>
+#else
+#include <dlfcn.h>
+#endif
+
+
+#if defined(_WIN32)
+#define OS_LIBRARY_EXTENSION ".dll"
+#elif defined(__APPLE__)
+#define OS_LIBRARY_EXTENSION ".dylib"
+#else
+#define OS_LIBRARY_EXTENSION ".so"
+#endif
+
+
+namespace os {
+
+ // TODO: Wrap in a class
+#if defined(_WIN32)
+ typedef HMODULE Library;
+#else
+ typedef void * Library;
+#endif
+
+ inline Library
+ openLibrary(const char *filename) {
+#if defined(_WIN32)
+ return LoadLibraryA(filename);
+#else
+ return dlopen(filename, RTLD_LOCAL | RTLD_LAZY);
+#endif
+ }
+
+ inline void *
+ getLibrarySymbol(Library library, const char *symbol) {
+#if defined(_WIN32)
+ return (void *)GetProcAddress(library, symbol);
+#else
+ return dlsym(library, symbol);
+#endif
+ }
+
+ inline void
+ closeLibrary(Library library) {
+#if defined(_WIN32)
+ FreeLibrary(library);
+#else
+ dlclose(library);
+#endif
+ }
+
+
+} /* namespace os */
+
diff --git a/lib/os/os_memory.hpp b/lib/os/os_memory.hpp
new file mode 100644
index 00000000..5a852516
--- /dev/null
+++ b/lib/os/os_memory.hpp
@@ -0,0 +1,78 @@
+/**************************************************************************
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reversed.
+ * Author: Shuang He <shuang.he@intel.com>
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Simple OS time measurement abstraction.
+ */
+
+#pragma once
+
+#ifdef HAVE_READPROC_H
+#include <proc/readproc.h>
+#endif
+
+namespace os {
+
+#if defined(HAVE_READPROC_H)
+
+ inline long long
+ getVsize(void) {
+ proc_t proc;
+ look_up_our_self(&proc);
+ return proc.vsize;
+ }
+
+ inline long long
+ getRss(void) {
+ proc_t proc;
+ look_up_our_self(&proc);
+ return proc.rss;
+ }
+
+#elif defined(__ANDROID__)
+
+ long long
+ getVsize(void);
+
+ long long
+ getRss(void);
+
+#else
+
+ inline long long
+ getVsize(void) {
+ return 0;
+ }
+
+ inline long long
+ getRss(void) {
+ return 0;
+ }
+
+#endif
+
+} /* namespace os */
+
diff --git a/lib/os/os_osx.mm b/lib/os/os_osx.mm
new file mode 100644
index 00000000..9901708e
--- /dev/null
+++ b/lib/os/os_osx.mm
@@ -0,0 +1,41 @@
+/**************************************************************************
+ *
+ * Copyright 2016 VMware, Inc.
+ * Copyright 2016 Rolan Reznik
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include "os_string.hpp"
+
+#include <Foundation/Foundation.h>
+
+namespace os {
+
+String
+getTemporaryDirectoryPath(void) {
+ NSString *tempDir = NSTemporaryDirectory();
+ String result([tempDir fileSystemRepresentation]);
+ return result;
+}
+
+}
diff --git a/lib/os/os_posix.cpp b/lib/os/os_posix.cpp
new file mode 100644
index 00000000..74594c3f
--- /dev/null
+++ b/lib/os/os_posix.cpp
@@ -0,0 +1,468 @@
+/**************************************************************************
+ *
+ * Copyright 2010-2011 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#ifndef _WIN32
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#if defined(__linux__)
+#include <linux/limits.h> // PATH_MAX
+#endif
+
+#ifdef __APPLE__
+#include <sys/syslimits.h> // PATH_MAX
+#include <mach-o/dyld.h>
+#endif
+
+#ifdef ANDROID
+#include <android/log.h>
+#endif
+
+#ifndef PATH_MAX
+#warning PATH_MAX undefined
+#define PATH_MAX 4096
+#endif
+
+#ifdef __QNXNTO__
+#define SA_RESTART 0 // QNX does not have SA_RESTART
+#endif
+
+#ifndef __has_feature
+# define __has_feature(x) 0
+#endif
+#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+# include <sanitizer/asan_interface.h>
+#endif
+
+#include "os.hpp"
+#include "os_string.hpp"
+#include "os_backtrace.hpp"
+
+
+namespace os {
+
+
+String
+getProcessName(void)
+{
+ String path;
+ size_t size = PATH_MAX;
+ char *buf = path.buf(size);
+
+ // http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe
+#ifdef __APPLE__
+ uint32_t len = size;
+ if (_NSGetExecutablePath(buf, &len) != 0) {
+ // grow buf and retry
+ buf = path.buf(len);
+ _NSGetExecutablePath(buf, &len);
+ }
+ len = strlen(buf);
+#else
+ ssize_t len;
+
+#ifdef ANDROID
+ // On Android, we are almost always interested in the actual process title
+ // rather than path to the VM kick-off executable
+ // (/system/bin/app_process).
+ len = 0;
+#else
+ len = readlink("/proc/self/exe", buf, size - 1);
+#endif
+
+ if (len <= 0) {
+ // /proc/self/exe is not available on setuid processes, so fallback to
+ // /proc/self/cmdline.
+ int fd = open("/proc/self/cmdline", O_RDONLY);
+ if (fd >= 0) {
+ // buf already includes trailing zero
+ len = read(fd, buf, size);
+ close(fd);
+ if (len >= 0) {
+ len = strlen(buf);
+ }
+ }
+ }
+
+#ifdef __GLIBC__
+ // fallback to `program_invocation_name`
+ if (len <= 0) {
+ len = strlen(program_invocation_name);
+ buf = path.buf(len + 1);
+ strcpy(buf, program_invocation_name);
+ }
+#endif
+
+ // fallback to process ID
+ if (len <= 0) {
+ len = snprintf(buf, size, "%i", (int)getpid());
+ if (len >= size) {
+ len = size - 1;
+ }
+ }
+#endif
+ path.truncate(len);
+
+ return path;
+}
+
+String
+getCurrentDir(void)
+{
+ String path;
+ size_t size = PATH_MAX;
+ char *buf = path.buf(size);
+
+ if (getcwd(buf, size)) {
+ buf[size - 1] = 0;
+ path.truncate();
+ } else {
+ path.truncate(0);
+ }
+
+ return path;
+}
+
+String
+getConfigDir(void)
+{
+ String path;
+
+#ifdef __APPLE__
+ // Library/Preferences
+ const char *homeDir = getenv("HOME");
+ assert(homeDir);
+ if (homeDir) {
+ path = homeDir;
+ path.join("Library/Preferences");
+ }
+#else
+ // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
+ const char *configHomeDir = getenv("XDG_CONFIG_HOME");
+ if (configHomeDir) {
+ path = configHomeDir;
+ } else {
+ const char *homeDir = getenv("HOME");
+ if (!homeDir) {
+ struct passwd *user = getpwuid(getuid());
+ if (user != NULL) {
+ homeDir = user->pw_dir;
+ }
+ }
+ assert(homeDir);
+ if (homeDir) {
+ path = homeDir;
+#if !defined(ANDROID)
+ path.join(".config");
+#endif
+ }
+ }
+#endif
+
+ return path;
+}
+
+bool
+createDirectory(const String &path)
+{
+ return mkdir(path, 0777) == 0;
+}
+
+bool
+String::exists(void) const
+{
+ struct stat st;
+ int err;
+
+ err = stat(str(), &st);
+ if (err) {
+ return false;
+ }
+
+ return true;
+}
+
+int execute(char * const * args)
+{
+ pid_t pid = fork();
+ if (pid == 0) {
+ // child
+ execvp(args[0], args);
+ fprintf(stderr, "error: failed to execute:");
+ for (unsigned i = 0; args[i]; ++i) {
+ fprintf(stderr, " %s", args[i]);
+ }
+ fprintf(stderr, "\n");
+ exit(-1);
+ } else {
+ // parent
+ if (pid == -1) {
+ fprintf(stderr, "error: failed to fork\n");
+ return -1;
+ }
+ int status = -1;
+ int ret;
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status)) {
+ ret = WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ // match shell return code
+ ret = WTERMSIG(status) + 128;
+ } else {
+ ret = 128;
+ }
+ return ret;
+ }
+}
+
+static volatile bool logging = false;
+
+#ifndef HAVE_EXTERNAL_OS_LOG
+void
+log(const char *format, ...)
+{
+ logging = true;
+ va_list ap;
+ va_start(ap, format);
+ fflush(stdout);
+#ifdef ANDROID
+ __android_log_vprint(ANDROID_LOG_DEBUG, "apitrace", format, ap);
+#else
+ static FILE *log = NULL;
+ if (!log) {
+ // Duplicate stderr file descriptor, to prevent applications from
+ // redirecting our debug messages to somewhere else.
+ //
+ // Another alternative would be to log to /dev/tty when available.
+ log = fdopen(dup(STDERR_FILENO), "at");
+ }
+ vfprintf(log, format, ap);
+ fflush(log);
+#endif
+ va_end(ap);
+ logging = false;
+}
+#endif /* !HAVE_EXTERNAL_OS_LOG */
+
+#if defined(__APPLE__)
+long long timeFrequency = 0LL;
+#endif
+
+void
+abort(void)
+{
+ _exit(1);
+}
+
+
+void
+breakpoint(void)
+{
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+ asm("int3");
+#else
+ kill(getpid(), SIGTRAP);
+#endif
+}
+
+
+static void (*gCallback)(void) = NULL;
+
+#define NUM_SIGNALS 16
+
+struct sigaction old_actions[NUM_SIGNALS];
+
+
+/*
+ * See also:
+ * - http://sourceware.org/git/?p=glibc.git;a=blob;f=debug/segfault.c
+ * - http://ggi.cvs.sourceforge.net/viewvc/ggi/ggi-core/libgg/gg/cleanup.c?view=markup
+ */
+static void
+signalHandler(int sig, siginfo_t *info, void *context)
+{
+ /*
+ * There are several signals that can happen when logging to stdout/stderr.
+ * For example, SIGPIPE will be emitted if stderr is a pipe with no
+ * readers. Therefore ignore any signal while logging by returning
+ * immediately, to prevent deadlocks.
+ */
+ if (logging) {
+ return;
+ }
+
+ static int recursion_count = 0;
+
+ log("apitrace: warning: caught signal %i\n", sig);
+
+ if (recursion_count) {
+ log("apitrace: warning: recursion handling signal %i\n", sig);
+ } else {
+ ++recursion_count;
+ if (gCallback)
+ gCallback();
+ os::dump_backtrace();
+ --recursion_count;
+ }
+
+ struct sigaction *old_action;
+ if (sig >= NUM_SIGNALS) {
+ /* This should never happen */
+ log("error: unexpected signal %i\n", sig);
+ raise(SIGKILL);
+ }
+ old_action = &old_actions[sig];
+
+ if (old_action->sa_flags & SA_SIGINFO) {
+ // Handler is in sa_sigaction
+ old_action->sa_sigaction(sig, info, context);
+ } else {
+ if (old_action->sa_handler == SIG_DFL) {
+ log("apitrace: info: taking default action for signal %i\n", sig);
+
+#if 1
+ struct sigaction dfl_action;
+ dfl_action.sa_handler = SIG_DFL;
+ sigemptyset (&dfl_action.sa_mask);
+ dfl_action.sa_flags = 0;
+ sigaction(sig, &dfl_action, NULL);
+
+ raise(sig);
+#else
+ raise(SIGKILL);
+#endif
+ } else if (old_action->sa_handler == SIG_IGN) {
+ /* ignore */
+ } else {
+ /* dispatch to handler */
+ old_action->sa_handler(sig);
+ }
+ }
+}
+
+void
+setExceptionCallback(void (*callback)(void))
+{
+ assert(!gCallback);
+ if (!gCallback) {
+ gCallback = callback;
+
+ struct sigaction new_action;
+ new_action.sa_sigaction = signalHandler;
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = SA_SIGINFO | SA_RESTART;
+
+
+ for (int sig = 1; sig < NUM_SIGNALS; ++sig) {
+ // SIGKILL and SIGSTOP can't be handled.
+ if (sig == SIGKILL || sig == SIGSTOP) {
+ continue;
+ }
+
+ /*
+ * SIGPIPE can be emitted when writing to stderr that is redirected
+ * to a pipe without readers. It is also very unlikely to ocurr
+ * inside graphics APIs, and most applications where it can occur
+ * normally already ignore it. In summary, it is unlikely that a
+ * SIGPIPE will cause abnormal termination, which it is likely that
+ * intercepting here will cause problems, so simple don't intercept
+ * it here.
+ */
+ if (sig == SIGPIPE) {
+ continue;
+ }
+
+ if (sigaction(sig, NULL, &old_actions[sig]) >= 0) {
+ sigaction(sig, &new_action, NULL);
+ }
+ }
+
+#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+ __asan_set_death_callback(callback);
+#endif
+ }
+}
+
+void
+resetExceptionCallback(void)
+{
+ gCallback = NULL;
+#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+ __asan_set_death_callback(NULL);
+#endif
+}
+
+#ifdef __ANDROID__
+#include "os_memory.hpp"
+#include <cassert>
+#include <cstdio>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+char statmBuff[256];
+static __uint64_t pageSize = sysconf(_SC_PAGESIZE);
+
+static long size, resident;
+
+static inline void parseStatm()
+{
+ int fd = open("/proc/self/statm", O_RDONLY, 0);
+ int sz = read(fd, statmBuff, 255);
+ close(fd);
+ statmBuff[sz] = 0;
+ sz = sscanf(statmBuff, "%ld %ld",
+ &size, &resident);
+ assert(sz == 2);
+}
+
+long long getVsize()
+{
+ parseStatm();
+ return pageSize * size;
+}
+
+long long getRss()
+{
+ parseStatm();
+ return pageSize * resident;
+}
+#endif
+
+} /* namespace os */
+
+#endif // !defined(_WIN32)
diff --git a/lib/os/os_process.hpp b/lib/os/os_process.hpp
new file mode 100644
index 00000000..880242f8
--- /dev/null
+++ b/lib/os/os_process.hpp
@@ -0,0 +1,89 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Jose Fonseca
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Sub-process abstraction.
+ */
+
+#pragma once
+
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#include "os.hpp"
+
+
+namespace os {
+
+
+typedef
+#ifdef _WIN32
+ DWORD
+#else
+ pid_t
+#endif
+ProcessId;
+
+
+static inline ProcessId
+getCurrentProcessId(void) {
+#ifdef _WIN32
+ return GetCurrentProcessId();
+#else
+ return getpid();
+#endif
+}
+
+
+static inline void
+setEnvironment(const char *name, const char *value) {
+#ifdef _WIN32
+ SetEnvironmentVariableA(name, value);
+#else
+ setenv(name, value, 1);
+#endif
+}
+
+
+static inline void
+unsetEnvironment(const char *name) {
+#ifdef _WIN32
+ SetEnvironmentVariableA(name, NULL);
+#else
+ unsetenv(name);
+#endif
+}
+
+
+int execute(char * const * args);
+
+
+} /* namespace os */
+
diff --git a/lib/os/os_string.hpp b/lib/os/os_string.hpp
new file mode 100644
index 00000000..2eb797bd
--- /dev/null
+++ b/lib/os/os_string.hpp
@@ -0,0 +1,437 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Jose Fonseca
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * String manipulation.
+ */
+
+#pragma once
+
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stddef.h>
+
+#ifdef __MINGW32__
+// Some versions of MinGW are missing _vscprintf's declaration, although they
+// still provide the symbol in the import library.
+extern "C" _CRTIMP int _vscprintf(const char *format, va_list argptr);
+#endif
+
+#ifndef va_copy
+#ifdef __va_copy
+#define va_copy(dest, src) __va_copy((dest), (src))
+#else
+#define va_copy(dest, src) (dest) = (src)
+#endif
+#endif
+
+#include <vector>
+
+#include "os.hpp"
+
+
+#ifdef _WIN32
+#define OS_DIR_SEP '\\'
+#define OS_PATH_SEP ';'
+#else /* !_WIN32 */
+#define OS_DIR_SEP '/'
+#define OS_PATH_SEP ':'
+#endif /* !_WIN32 */
+
+
+namespace os {
+
+
+/**
+ * Class to represent zero-terminated strings, based upon std::vector<char>,
+ * suitable for passing strings or paths to/from OS calls.
+ *
+ * Both Win32 and POSIX APIs return strings as zero length buffers. Although
+ * std::string provides an easy method to obtain a read-only pointer to a zero
+ * terminated string, it lacks the ability to return a read-write pointer. So
+ * there is no way to tell OS calls to write into a std::string directly -- a
+ * temporary malloc'ed string would be necessary --, which would be
+ * unnecessarily inefficient, specially considering that these strings would
+ * ultimately passed back to the OS, which would again expect zero-terminated
+ * strings.
+ *
+ * This class is not, however, a full replacement for std::string, which should
+ * be otherwise used whenever possible.
+ */
+class String {
+protected:
+ typedef std::vector<char> Buffer;
+
+ /**
+ * The buffer's last element is always the '\0' character, therefore the
+ * buffer must never be empty.
+ */
+ Buffer buffer;
+
+ Buffer::iterator find(char c) {
+ Buffer::iterator it = buffer.begin();
+ assert(it != buffer.end());
+ while (it != buffer.end()) {
+ if (*it == c) {
+ return it;
+ }
+ ++it;
+ }
+ return buffer.end();
+ }
+
+ Buffer::iterator rfind(char c) {
+ Buffer::iterator it = buffer.end();
+
+ // Skip trailing '\0'
+ assert(it != buffer.begin());
+ --it;
+ assert(*it == '\0');
+
+ while (it != buffer.begin()) {
+ --it;
+ if (*it == c) {
+ return it;
+ }
+ }
+
+ return buffer.end();
+ }
+
+ String(size_t size) :
+ buffer(size) {
+ }
+
+ char *buf(void) {
+ return &buffer[0];
+ }
+
+ inline bool
+ isSep(char c) {
+ if (c == '/') {
+ return true;
+ }
+#ifdef _WIN32
+ if (c == '\\') {
+ return true;
+ }
+#endif
+ return false;
+ }
+
+public:
+
+ Buffer::iterator rfindSep(bool skipTrailing = true) {
+ Buffer::iterator it = end();
+
+ // Skip trailing separators
+ if (skipTrailing) {
+ while (it != buffer.begin()) {
+ --it;
+ if (isSep(*it)) {
+ // Halt if find the root
+ if (it == buffer.begin()) {
+ return it;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ // Advance to the last separator
+ while (it != buffer.begin()) {
+ --it;
+ if (isSep(*it)) {
+ return it;
+ }
+ }
+
+ return end();
+ }
+
+ /*
+ * Constructors
+ */
+
+ String(void) {
+ buffer.push_back(0);
+ }
+
+ String(const char *s) :
+ buffer(s, s + strlen(s) + 1)
+ {}
+
+ String(const String &other) :
+ buffer(other.buffer)
+ {}
+
+ template <class InputIterator>
+ String(InputIterator first, InputIterator last) :
+ buffer(first, last)
+ {
+ buffer.push_back(0);
+ }
+
+ /**
+ * From a printf-like format string
+ */
+ static String
+ format(const char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 1, 2)))
+#endif
+ {
+
+ va_list args;
+
+ va_start(args, format);
+
+ int length;
+ va_list args_copy;
+ va_copy(args_copy, args);
+#ifdef _WIN32
+ /* We need to use _vscprintf to calculate the length as vsnprintf returns -1
+ * if the number of characters to write is greater than count.
+ */
+ length = _vscprintf(format, args_copy);
+#else
+ char dummy;
+ length = vsnprintf(&dummy, sizeof dummy, format, args_copy);
+#endif
+ va_end(args_copy);
+
+ assert(length >= 0);
+ size_t size = length + 1;
+
+ String path(size);
+
+ va_start(args, format);
+ vsnprintf(path.buf(), size, format, args);
+ va_end(args);
+
+ return path;
+ }
+
+ /*
+ * Conversion to ordinary C strings.
+ */
+
+ const char *str(void) const {
+ assert(buffer.back() == 0);
+ return &buffer[0];
+ }
+
+ operator const char *(void) const {
+ return str();
+ }
+
+ /*
+ * Iterators
+ */
+
+ typedef Buffer::const_iterator const_iterator;
+ typedef Buffer::iterator iterator;
+
+ const_iterator begin(void) const {
+ return buffer.begin();
+ }
+
+ iterator begin(void) {
+ return buffer.begin();
+ }
+
+ const_iterator end(void) const {
+ const_iterator it = buffer.end();
+ assert(it != buffer.begin());
+ --it; // skip null
+ return it;
+ }
+
+ iterator end(void) {
+ iterator it = buffer.end();
+ assert(it != buffer.begin());
+ --it; // skip null
+ return it;
+ }
+
+ /*
+ * Operations
+ */
+
+ void insert(iterator position, char c) {
+ buffer.insert(position, c);
+ }
+
+ template <class InputIterator>
+ void insert(iterator position, InputIterator first, InputIterator last) {
+ buffer.insert(position, first, last);
+ }
+
+ void insert(iterator position, const char *s) {
+ assert(s);
+ insert(position, s, s + strlen(s));
+ }
+
+ void insert(iterator position, const String & other) {
+ insert(position, other.begin(), other.end());
+ }
+
+ void append(char c) {
+ insert(end(), c);
+ }
+
+ template <class InputIterator>
+ void append(InputIterator first, InputIterator last) {
+ insert(end(), first, last);
+ }
+
+ void append(const char *s) {
+ insert(end(), s);
+ }
+
+ void append(const String & other) {
+ insert(end(), other);
+ }
+
+ template <class InputIterator>
+ void erase(InputIterator first, InputIterator last) {
+ buffer.erase(first, last);
+ }
+
+ /**
+ * Get a writable buffer with the specified size.
+ *
+ * truncate() must be called after the buffer is written, and before any other
+ * method is called.
+ *
+ * Between the call to buf() and truncate() methods, the `buffer.back() ==
+ * 0` invariant will not hold true.
+ */
+ char *buf(size_t size) {
+ buffer.resize(size);
+ return &buffer[0];
+ }
+
+ size_t length(void) const {
+ size_t size = buffer.size();
+ assert(size > 0);
+ assert(buffer[size - 1] == 0);
+ return size - 1;
+ }
+
+ /**
+ * Truncate the string to the specified length.
+ */
+ void truncate(size_t length) {
+ assert(length < buffer.size());
+ buffer[length] = 0;
+ assert(strlen(&buffer[0]) == length);
+ buffer.resize(length + 1);
+ }
+
+ /**
+ * Truncate the string to the first zero character.
+ */
+ void truncate(void) {
+ truncate(strlen(&buffer[0]));
+ }
+
+
+ /*
+ * Path manipulation
+ */
+
+ bool
+ exists(void) const;
+
+ /* Trim directory (leaving base filename).
+ */
+ void trimDirectory(void) {
+ iterator sep = rfindSep();
+ if (sep != end()) {
+ buffer.erase(buffer.begin(), sep + 1);
+ }
+ }
+
+ /* Trim filename component (leaving containing directory).
+ *
+ * - trailing separators are ignored
+ * - a path with no separator at all yields "."
+ * - a path consisting of just the root directory is left unchanged
+ */
+ void trimFilename(void) {
+ iterator sep = rfindSep();
+
+ // No separator found, so return '.'
+ if (sep == end()) {
+ buffer.resize(2);
+ buffer[0] = '.';
+ buffer[1] = 0;
+ return;
+ }
+
+ // Root. Nothing to do.
+ if (sep == buffer.begin()) {
+ return;
+ }
+
+ // Trim filename
+ buffer.erase(sep, end());
+ }
+
+ void trimExtension(void) {
+ iterator dot = rfind('.');
+ if (dot != buffer.end()) {
+ buffer.erase(dot, end());
+ }
+ }
+
+ void join(const String & other) {
+ if (length() && end()[-1] != OS_DIR_SEP) {
+ append(OS_DIR_SEP);
+ }
+ append(other.begin(), other.end());
+ }
+};
+
+
+String getProcessName();
+String getCurrentDir();
+
+String getConfigDir();
+
+bool createDirectory(const String &path);
+
+bool copyFile(const String &srcFileName, const String &dstFileName, bool override = true);
+
+bool removeFile(const String &fileName);
+
+String getTemporaryDirectoryPath(void);
+
+} /* namespace os */
+
diff --git a/lib/os/os_thread.hpp b/lib/os/os_thread.hpp
new file mode 100644
index 00000000..fa080b91
--- /dev/null
+++ b/lib/os/os_thread.hpp
@@ -0,0 +1,539 @@
+/**************************************************************************
+ *
+ * Copyright 2011-2012 Jose Fonseca
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * OS native thread abstraction.
+ *
+ * Mimics/leverages C++11 threads.
+ */
+
+#pragma once
+
+
+/* XXX: We still use our own implementation:
+ *
+ * - MSVC's C++11 threads implementation are hardcoded to use C++ exceptions
+ *
+ * - MinGW's C++11 threads implementation is often either missing or relies on
+ * winpthreads
+ */
+
+#if !defined(_WIN32)
+#define HAVE_CXX11_THREADS
+#endif
+
+
+#ifdef HAVE_CXX11_THREADS
+
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+
+namespace os {
+
+ using std::mutex;
+ using std::recursive_mutex;
+ using std::unique_lock;
+ using std::condition_variable;
+ using std::thread;
+
+} /* namespace os */
+
+
+#else /* !HAVE_CXX11_THREADS */
+
+
+#include <assert.h>
+#ifdef _WIN32
+# include <process.h>
+# include <windows.h>
+# if _WIN32_WINNT >= 0x0600
+# define HAVE_WIN32_CONDITION_VARIABLES
+# endif
+#else
+# include <pthread.h>
+# include <unistd.h>
+#endif
+
+#include <functional>
+
+
+namespace os {
+
+
+ /**
+ * Base class for mutex and recursive_mutex.
+ */
+ class _base_mutex
+ {
+ public:
+#ifdef _WIN32
+ typedef CRITICAL_SECTION native_handle_type;
+#else
+ typedef pthread_mutex_t native_handle_type;
+#endif
+
+ protected:
+ _base_mutex(void) {
+ }
+
+ public:
+ ~_base_mutex() {
+#ifdef _WIN32
+ DeleteCriticalSection(&_native_handle);
+#else
+ pthread_mutex_destroy(&_native_handle);
+#endif
+ }
+
+ inline void
+ lock(void) {
+#ifdef _WIN32
+ EnterCriticalSection(&_native_handle);
+#else
+ pthread_mutex_lock(&_native_handle);
+#endif
+ }
+
+ inline void
+ unlock(void) {
+#ifdef _WIN32
+ LeaveCriticalSection(&_native_handle);
+#else
+ pthread_mutex_unlock(&_native_handle);
+#endif
+ }
+
+ native_handle_type & native_handle() {
+ return _native_handle;
+ }
+
+ protected:
+ native_handle_type _native_handle;
+ };
+
+
+ /**
+ * Same interface as std::mutex.
+ */
+ class mutex : public _base_mutex
+ {
+ public:
+ inline
+ mutex(void) {
+#ifdef _WIN32
+ InitializeCriticalSection(&_native_handle);
+#else
+ pthread_mutex_init(&_native_handle, NULL);
+#endif
+ }
+ };
+
+
+ /**
+ * Same interface as std::recursive_mutex.
+ */
+ class recursive_mutex : public _base_mutex
+ {
+ public:
+ inline
+ recursive_mutex(void) {
+#ifdef _WIN32
+ InitializeCriticalSection(&_native_handle);
+#else
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&_native_handle, &attr);
+ pthread_mutexattr_destroy(&attr);
+#endif
+ }
+ };
+
+
+ /**
+ * Same interface as std::unique_lock;
+ */
+ template< class Mutex >
+ class unique_lock
+ {
+ public:
+ typedef Mutex mutex_type;
+
+ inline explicit
+ unique_lock(mutex_type &m) :
+ _mutex(m)
+ {
+ _mutex.lock();
+ }
+
+ inline
+ ~unique_lock() {
+ _mutex.unlock();
+ }
+
+ inline void
+ lock() {
+ _mutex.lock();
+ }
+
+ inline void
+ unlock() {
+ _mutex.unlock();
+ }
+
+ mutex_type *
+ mutex() const {
+ return &_mutex;
+ }
+
+ protected:
+ mutex_type &_mutex;
+ };
+
+
+ /**
+ * Same interface as std::condition_variable
+ */
+ class condition_variable
+ {
+ private:
+#ifdef _WIN32
+# ifdef HAVE_WIN32_CONDITION_VARIABLES
+ // Only supported on Vista an higher. Not yet supported by WINE.
+ typedef CONDITION_VARIABLE native_handle_type;
+ native_handle_type _native_handle;
+#else
+ // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
+ LONG cWaiters;
+ enum {
+ EVENT_ONE = 0,
+ EVENT_ALL,
+ EVENT_COUNT
+ };
+ HANDLE hEvents[EVENT_COUNT];
+#endif
+#else
+ typedef pthread_cond_t native_handle_type;
+ native_handle_type _native_handle;
+#endif
+
+ public:
+ condition_variable() {
+#ifdef _WIN32
+# ifdef HAVE_WIN32_CONDITION_VARIABLES
+ InitializeConditionVariable(&_native_handle);
+# else
+ cWaiters = 0;
+ hEvents[EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ hEvents[EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
+# endif
+#else
+ pthread_cond_init(&_native_handle, NULL);
+#endif
+ }
+
+ ~condition_variable() {
+#ifdef _WIN32
+# ifdef HAVE_WIN32_CONDITION_VARIABLES
+ /* No-op */
+# else
+ CloseHandle(hEvents[EVENT_ALL]);
+ CloseHandle(hEvents[EVENT_ONE]);
+# endif
+#else
+ pthread_cond_destroy(&_native_handle);
+#endif
+ }
+
+ inline void
+ notify_one(void) {
+#ifdef _WIN32
+# ifdef HAVE_WIN32_CONDITION_VARIABLES
+ WakeConditionVariable(&_native_handle);
+# else
+ if (cWaiters) {
+ SetEvent(hEvents[EVENT_ONE]);
+ }
+# endif
+#else
+ pthread_cond_signal(&_native_handle);
+#endif
+ }
+
+ inline void
+ notify_all(void) {
+#ifdef _WIN32
+# ifdef HAVE_WIN32_CONDITION_VARIABLES
+ WakeAllConditionVariable(&_native_handle);
+# else
+ if (cWaiters) {
+ SetEvent(hEvents[EVENT_ALL]);
+ }
+# endif
+#else
+ pthread_cond_broadcast(&_native_handle);
+#endif
+ }
+
+ inline void
+ wait(unique_lock<mutex> & lock) {
+ mutex::native_handle_type & mutex_native_handle = lock.mutex()->native_handle();
+#ifdef _WIN32
+# ifdef HAVE_WIN32_CONDITION_VARIABLES
+ SleepConditionVariableCS(&_native_handle, &mutex_native_handle, INFINITE);
+# else
+ InterlockedIncrement(&cWaiters);
+ LeaveCriticalSection(&mutex_native_handle);
+ DWORD dwResult;
+ dwResult = WaitForMultipleObjects(EVENT_COUNT, hEvents, FALSE, INFINITE);
+ EnterCriticalSection(&mutex_native_handle);
+ if (InterlockedDecrement(&cWaiters) == 0 &&
+ dwResult == WAIT_OBJECT_0 + EVENT_ALL) {
+ ResetEvent(hEvents[EVENT_ALL]);
+ }
+# endif
+#else
+ pthread_cond_wait(&_native_handle, &mutex_native_handle);
+#endif
+ }
+
+ inline void
+ wait(unique_lock<mutex> & lock, std::function<bool()> pred) {
+ while (!pred) {
+ wait(lock);
+ }
+ }
+ };
+
+
+ /**
+ * Implement TLS through OS threading API.
+ *
+ * This will only work when T is a pointer, intptr_t, or uintptr_t.
+ */
+ template <typename T>
+ class thread_specific
+ {
+ private:
+ static_assert(sizeof(T) == sizeof(void *), "Size mismatch");
+
+#ifdef _WIN32
+ DWORD dwTlsIndex;
+#else
+ pthread_key_t key;
+#endif
+
+ public:
+ thread_specific(void) {
+#ifdef _WIN32
+ dwTlsIndex = TlsAlloc();
+#else
+ pthread_key_create(&key, NULL);
+#endif
+ }
+
+ ~thread_specific() {
+#ifdef _WIN32
+ TlsFree(dwTlsIndex);
+#else
+ pthread_key_delete(key);
+#endif
+ }
+
+ inline T
+ get(void) const {
+ void *ptr;
+#ifdef _WIN32
+ ptr = TlsGetValue(dwTlsIndex);
+#else
+ ptr = pthread_getspecific(key);
+#endif
+ return reinterpret_cast<T>(ptr);
+ }
+
+ inline
+ operator T (void) const
+ {
+ return get();
+ }
+
+ inline T
+ operator -> (void) const
+ {
+ return get();
+ }
+
+ inline T
+ operator = (T new_value)
+ {
+ set(new_value);
+ return new_value;
+ }
+
+ inline void
+ set(T new_value) {
+ void *new_ptr = reinterpret_cast<void *>(new_value);
+#ifdef _WIN32
+ TlsSetValue(dwTlsIndex, new_ptr);
+#else
+ pthread_setspecific(key, new_ptr);
+#endif
+ }
+ };
+
+
+ /**
+ * Same interface as std::thread
+ */
+ class thread {
+ public:
+#ifdef _WIN32
+ typedef HANDLE native_handle_type;
+#else
+ typedef pthread_t native_handle_type;
+#endif
+
+ inline
+ thread() :
+ _native_handle(0)
+ {
+ }
+
+ inline
+ thread(thread &&other) :
+ _native_handle(other._native_handle)
+ {
+ other._native_handle = 0;
+ }
+
+ thread(const thread &other) = delete;
+
+ inline
+ ~thread() {
+ }
+
+ static unsigned
+ hardware_concurrency(void) {
+#ifdef _WIN32
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ return si.dwNumberOfProcessors;
+#else
+ return sysconf(_SC_NPROCESSORS_ONLN);
+#endif
+ }
+
+ template< class Function, class... Args >
+ explicit thread(Function &&f, Args&&... args) {
+ auto bound = std::bind(std::forward<Function>(f), std::forward<Args>(args)...);
+ auto data = new decltype(bound) (std::move(bound));
+ _native_handle = _create(data);
+ }
+
+ inline thread &
+ operator =(thread &&other) {
+ assert(_native_handle == 0);
+ _native_handle = other._native_handle;
+ other._native_handle = 0;
+ return *this;
+ }
+
+ inline bool
+ joinable(void) const {
+ return _native_handle != 0;
+ }
+
+ inline void
+ join() {
+#ifdef _WIN32
+ WaitForSingleObject(_native_handle, INFINITE);
+#else
+ pthread_join(_native_handle, NULL);
+#endif
+ }
+
+ private:
+ native_handle_type _native_handle;
+
+ template< typename Param >
+ static
+#ifdef _WIN32
+ unsigned __stdcall
+#else
+ void *
+#endif
+ _callback(void *lpParameter) {
+ Param *pParam = static_cast<Param *>(lpParameter);
+ (*pParam)();
+ delete pParam;
+ return 0;
+ }
+
+ template< typename Param >
+ static inline native_handle_type
+ _create(Param *function) {
+#ifdef _WIN32
+ uintptr_t handle =_beginthreadex(NULL, 0, &_callback<Param>, function, 0, NULL);
+ return reinterpret_cast<HANDLE>(handle);
+#else
+ pthread_t t;
+ pthread_create(&t, NULL, &_callback<Param>, function);
+ return t;
+#endif
+ }
+ };
+
+} /* namespace os */
+
+
+#endif /* !HAVE_CXX11_THREADS */
+
+
+/**
+ * Compiler TLS.
+ *
+ * It's not portable to use for DLLs on Windows XP, or non-POD types.
+ *
+ * See also:
+ * - http://gcc.gnu.org/onlinedocs/gcc-4.6.3/gcc/Thread_002dLocal.html
+ * - http://msdn.microsoft.com/en-us/library/9w1sdazb.aspx
+ * - https://msdn.microsoft.com/en-us/library/y5f6w579.aspx
+ */
+#if defined(__GNUC__)
+# define OS_THREAD_LOCAL __thread
+#elif defined(_MSC_VER)
+# define OS_THREAD_LOCAL __declspec(thread)
+#else
+# error
+#endif
+
+#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0600
+# define OS_THREAD_SPECIFIC(_type) os::thread_specific< _type >
+#else
+# define OS_THREAD_SPECIFIC(_type) OS_THREAD_LOCAL _type
+#endif
+
+#define OS_THREAD_SPECIFIC_PTR(_type) OS_THREAD_SPECIFIC(_type *)
diff --git a/lib/os/os_thread_test.cpp b/lib/os/os_thread_test.cpp
new file mode 100644
index 00000000..7385b00c
--- /dev/null
+++ b/lib/os/os_thread_test.cpp
@@ -0,0 +1,161 @@
+/**************************************************************************
+ *
+ * Copyright 2016 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include "os_thread.hpp"
+
+#include "gtest/gtest.h"
+
+TEST(os_thread, hardware_concurrency)
+{
+ EXPECT_GT(os::thread::hardware_concurrency(), 0);
+}
+
+
+static bool b0;
+static void f0(void) {
+ b0 = true;
+}
+
+static bool b1;
+static void f1(int a1) {
+ b1 = a1 == 1;
+}
+
+static bool b2;
+static void f2(int a2, int a3) {
+ b2 = a2 == 2 && a3 == 3;
+}
+
+TEST(os_thread, thread)
+{
+ os::thread t0(&f0);
+ t0.join();
+ EXPECT_TRUE(b0);
+
+ os::thread t1(&f1, 1);
+ t1.join();
+ EXPECT_TRUE(b1);
+
+ os::thread t2(&f2, 2, 3);
+ t2.join();
+ EXPECT_TRUE(b2);
+}
+
+#define NUM_THREADS 4
+
+
+struct Data
+{
+ bool notified[NUM_THREADS];
+ os::condition_variable cv;
+ os::mutex mutex;
+ bool notify = false;
+ volatile unsigned waiting = 0;
+ volatile unsigned c = 0;
+};
+
+#if 0
+ static os::mutex cerr_mutex;
+# define WITH_CERR_MUTEX(_stmts) \
+ { \
+ os::unique_lock<os::mutex> cerr_lock(cerr_mutex); \
+ _stmts \
+ }
+#else
+# define WITH_CERR_MUTEX(_stmts)
+#endif
+
+
+static void cvf(Data *data, unsigned idx)
+{
+ WITH_CERR_MUTEX( std::cerr << idx << " started.\n"; )
+
+ {
+ os::unique_lock<os::mutex> l(data->mutex);
+ data->waiting += 1;
+ }
+
+ WITH_CERR_MUTEX( std::cerr << idx << " waiting...\n"; )
+
+ {
+ os::unique_lock<os::mutex> lock(data->mutex);
+ while (!data->notify) {
+ data->cv.wait(lock);
+ }
+ data->notified[idx] = true;
+ }
+
+ WITH_CERR_MUTEX( std::cerr << idx << " notified.\n"; )
+}
+
+
+TEST(os_thread, condition_variable)
+{
+ os::thread t[NUM_THREADS];
+ Data data;
+ unsigned i;
+
+ for (i = 0; i < NUM_THREADS; ++i) {
+ data.notified[i] = false;
+ t[i] = os::thread(cvf, &data, i);
+ }
+
+ for (i = 0; i < NUM_THREADS; ++i) {
+ EXPECT_FALSE(data.notified[i]);
+ }
+
+ WITH_CERR_MUTEX( std::cerr << "M waiting...\n"; )
+
+ do {
+ os::unique_lock<os::mutex> l(data.mutex);
+ if (data.waiting == NUM_THREADS) {
+ break;
+ }
+ } while(true);
+
+ WITH_CERR_MUTEX( std::cerr << "M notifying...\n"; )
+
+ {
+ os::unique_lock<os::mutex> l(data.mutex);
+ data.notify = true;
+ }
+
+ data.cv.notify_all();
+
+ WITH_CERR_MUTEX( std::cerr << "M joining...\n"; )
+ for (i = 0; i < NUM_THREADS; ++i) {
+ t[i].join();
+ EXPECT_TRUE(data.notified[i]);
+ }
+}
+
+
+int
+main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/lib/os/os_time.hpp b/lib/os/os_time.hpp
new file mode 100644
index 00000000..e3f63aff
--- /dev/null
+++ b/lib/os/os_time.hpp
@@ -0,0 +1,104 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Jose Fonseca
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Simple OS time measurement abstraction.
+ */
+
+#pragma once
+
+
+#if defined(_WIN32)
+# include <windows.h>
+#else
+# if defined(__linux__)
+# include <time.h>
+# elif defined(__APPLE__)
+# include <mach/mach_time.h>
+# else
+# include <sys/time.h>
+# endif
+# include <unistd.h>
+#endif
+
+
+namespace os {
+
+ // OS dependent time frequency
+#if defined(_WIN32) || defined(__APPLE__)
+ // runtime variable on Windows and MacOSX
+ extern long long timeFrequency;
+#elif defined(__linux__)
+ // nanoseconds on Linux
+ static const long long timeFrequency = 1000000000LL;
+#else
+ // microseconds on Unices
+ static const long long timeFrequency = 1000000LL;
+#endif
+
+ // Time from an unknown base in a unit determined by timeFrequency
+ inline long long
+ getTime(void) {
+#if defined(_WIN32)
+ if (!timeFrequency) {
+ LARGE_INTEGER frequency;
+ QueryPerformanceFrequency(&frequency);
+ timeFrequency = frequency.QuadPart;
+ }
+ LARGE_INTEGER counter;
+ QueryPerformanceCounter(&counter);
+ return counter.QuadPart;
+#elif defined(__linux__)
+ struct timespec tp;
+ if (clock_gettime(CLOCK_MONOTONIC, &tp) == -1) {
+ return 0;
+ }
+ return tp.tv_sec * 1000000000LL + tp.tv_nsec;
+#elif defined(__APPLE__)
+ if (!timeFrequency) {
+ mach_timebase_info_data_t timebaseInfo;
+ mach_timebase_info(&timebaseInfo);
+ timeFrequency = 1000000000LL * timebaseInfo.denom / timebaseInfo.numer;
+ }
+ return mach_absolute_time();
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000000LL + tv.tv_usec;
+#endif
+ }
+
+ // Suspend execution
+ inline void
+ sleep(unsigned long usecs) {
+#if defined(_WIN32)
+ Sleep((usecs + 999) / 1000);
+#else
+ usleep(usecs);
+#endif
+ }
+
+} /* namespace os */
+
diff --git a/lib/os/os_version.hpp b/lib/os/os_version.hpp
new file mode 100644
index 00000000..ad85de7d
--- /dev/null
+++ b/lib/os/os_version.hpp
@@ -0,0 +1,61 @@
+/**************************************************************************
+ *
+ * Copyright 2014 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#pragma once
+
+
+#ifdef _WIN32
+
+
+#include <windows.h>
+
+
+/**
+ * VersionHelpers.h is not yet widely available (only available on certain MSVC
+ * and Windows SDK versions), so just define our own helpers.
+ *
+ * See http://msdn.microsoft.com/en-gb/library/windows/desktop/ms725491.aspx
+ */
+static inline bool
+IsWindows8OrGreater()
+{
+ OSVERSIONINFOEXW osvi;
+ ZeroMemory(&osvi, sizeof osvi);
+ osvi.dwOSVersionInfoSize = sizeof osvi;
+ osvi.dwMajorVersion = 6;
+ osvi.dwMinorVersion = 2;
+ osvi.wServicePackMajor = 0;
+ DWORDLONG dwlConditionMask = 0;
+ VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
+ VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
+ VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
+ return VerifyVersionInfoW(&osvi,
+ VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR,
+ dwlConditionMask);
+}
+
+
+#endif /* _WIN32 */
diff --git a/lib/os/os_win32.cpp b/lib/os/os_win32.cpp
new file mode 100644
index 00000000..66492ca8
--- /dev/null
+++ b/lib/os/os_win32.cpp
@@ -0,0 +1,405 @@
+/**************************************************************************
+ *
+ * Copyright 2010-2015 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#ifdef _WIN32
+
+#include <windows.h>
+#include <shlobj.h>
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <string>
+
+#include "os.hpp"
+#include "os_string.hpp"
+
+
+namespace os {
+
+
+String
+getProcessName(void)
+{
+ String path;
+ DWORD size = MAX_PATH;
+ char *buf = path.buf(size);
+
+ DWORD nWritten = GetModuleFileNameA(NULL, buf, size);
+ (void)nWritten;
+
+ path.truncate();
+
+ return path;
+}
+
+String
+getCurrentDir(void)
+{
+ String path;
+ DWORD size = MAX_PATH;
+ char *buf = path.buf(size);
+
+ DWORD ret = GetCurrentDirectoryA(size, buf);
+ (void)ret;
+
+ buf[size - 1] = 0;
+ path.truncate();
+
+ return path;
+}
+
+String
+getConfigDir(void)
+{
+ String path;
+ char *buf = path.buf(MAX_PATH);
+ HRESULT hr = SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, buf);
+ if (SUCCEEDED(hr)) {
+ path.truncate();
+ } else {
+ path.truncate(0);
+ }
+ return path;
+}
+
+bool
+createDirectory(const String &path)
+{
+ return CreateDirectoryA(path, NULL);
+}
+
+bool
+String::exists(void) const
+{
+ DWORD attrs = GetFileAttributesA(str());
+ return attrs != INVALID_FILE_ATTRIBUTES;
+}
+
+bool
+copyFile(const String &srcFileName, const String &dstFileName, bool override)
+{
+ return CopyFileA(srcFileName, dstFileName, !override);
+}
+
+bool
+removeFile(const String &srcFilename)
+{
+ return DeleteFileA(srcFilename);
+}
+
+/**
+ * Determine whether an argument should be quoted.
+ */
+static bool
+needsQuote(const char *arg)
+{
+ char c;
+ while (true) {
+ c = *arg++;
+ if (c == '\0') {
+ break;
+ }
+ if (c == ' ' || c == '\t' || c == '\"') {
+ return true;
+ }
+ if (c == '\\') {
+ c = *arg++;
+ if (c == '\0') {
+ break;
+ }
+ if (c == '"') {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void
+quoteArg(std::string &s, const char *arg)
+{
+ char c;
+ unsigned backslashes = 0;
+
+ s.push_back('"');
+ while (true) {
+ c = *arg++;
+ if (c == '\0') {
+ break;
+ } else if (c == '"') {
+ while (backslashes) {
+ s.push_back('\\');
+ --backslashes;
+ }
+ s.push_back('\\');
+ } else {
+ if (c == '\\') {
+ ++backslashes;
+ } else {
+ backslashes = 0;
+ }
+ }
+ s.push_back(c);
+ }
+ s.push_back('"');
+}
+
+int execute(char * const * args)
+{
+ std::string commandLine;
+
+ const char *arg0 = *args;
+ const char *arg;
+ char sep = 0;
+ while ((arg = *args++) != NULL) {
+ if (sep) {
+ commandLine.push_back(sep);
+ }
+
+ if (needsQuote(arg)) {
+ quoteArg(commandLine, arg);
+ } else {
+ commandLine.append(arg);
+ }
+
+ sep = ' ';
+ }
+
+ STARTUPINFOA startupInfo;
+ memset(&startupInfo, 0, sizeof(startupInfo));
+ startupInfo.cb = sizeof(startupInfo);
+
+ PROCESS_INFORMATION processInformation;
+
+ if (!CreateProcessA(NULL,
+ const_cast<char *>(commandLine.c_str()), // only modified by CreateProcessW
+ 0, // process attributes
+ 0, // thread attributes
+ FALSE, // inherit handles
+ 0, // creation flags,
+ NULL, // environment
+ NULL, // current directory
+ &startupInfo,
+ &processInformation
+ )) {
+ log("error: failed to execute %s\n", arg0);
+ return -1;
+ }
+
+ WaitForSingleObject(processInformation.hProcess, INFINITE);
+
+ DWORD exitCode = ~0UL;
+ GetExitCodeProcess(processInformation.hProcess, &exitCode);
+
+ CloseHandle(processInformation.hProcess);
+ CloseHandle(processInformation.hThread);
+
+ return (int)exitCode;
+}
+
+#ifndef HAVE_EXTERNAL_OS_LOG
+void
+log(const char *format, ...)
+{
+ char buf[4096];
+
+ va_list ap;
+ va_start(ap, format);
+ fflush(stdout);
+ vsnprintf(buf, sizeof buf, format, ap);
+ va_end(ap);
+
+ OutputDebugStringA(buf);
+
+ /*
+ * Also write the message to stderr, when a debugger is not present (to
+ * avoid duplicate messages in command line debuggers).
+ */
+#if _WIN32_WINNT > 0x0400
+ if (!IsDebuggerPresent()) {
+ fflush(stdout);
+ fputs(buf, stderr);
+ fflush(stderr);
+ }
+#endif
+}
+#endif /* !HAVE_EXTERNAL_OS_LOG */
+
+long long timeFrequency = 0LL;
+
+void
+abort(void)
+{
+ TerminateProcess(GetCurrentProcess(), 3);
+#if defined(__GNUC__)
+ __builtin_unreachable();
+#endif
+}
+
+
+void
+breakpoint(void)
+{
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+ asm("int3");
+#elif defined(_MSC_VER)
+ __debugbreak();
+#else
+ DebugBreak();
+#endif
+}
+
+
+#ifndef DBG_PRINTEXCEPTION_C
+#define DBG_PRINTEXCEPTION_C 0x40010006
+#endif
+
+#ifndef DBG_PRINTEXCEPTION_WIDE_C
+#define DBG_PRINTEXCEPTION_WIDE_C 0x4001000A
+#endif
+
+static PVOID prevExceptionFilter = NULL;
+static void (*gCallback)(void) = NULL;
+
+static LONG CALLBACK
+unhandledExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
+{
+ /*
+ * Before Vista KiUserExceptionDispatcher does not clear the direction
+ * flag.
+ *
+ * See also:
+ * - https://bugs.chromium.org/p/nativeclient/issues/detail?id=1495
+ */
+#ifdef _MSC_VER
+#ifndef _WIN64
+ __asm cld;
+#endif
+#else
+ asm("cld");
+#endif
+
+ PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
+ DWORD ExceptionCode = pExceptionRecord->ExceptionCode;
+
+ // https://msdn.microsoft.com/en-us/library/het71c37.aspx
+ switch (ExceptionCode >> 30) {
+ case 0: // success
+ return EXCEPTION_CONTINUE_SEARCH;
+ case 1: // informational
+ case 2: // warning
+ case 3: // error
+ break;
+ }
+
+ /*
+ * Ignore OutputDebugStringA/W exceptions.
+ */
+ if (ExceptionCode == DBG_PRINTEXCEPTION_C ||
+ ExceptionCode == DBG_PRINTEXCEPTION_WIDE_C) {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ /*
+ * Ignore C++ exceptions, as some applications generate a lot of these with
+ * no harm. But don't ignore them on debug builds, as bugs in apitrace can
+ * easily lead the applicationa and/or runtime to raise them, and catching
+ * them helps debugging.
+ *
+ * See also:
+ * - http://blogs.msdn.com/b/oldnewthing/archive/2010/07/30/10044061.aspx
+ * - http://support.microsoft.com/kb/185294
+ */
+#ifdef NDEBUG
+ if (ExceptionCode == 0xe06d7363) {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+#endif
+
+ /*
+ * Ignore thread naming exception.
+ *
+ * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
+ */
+ if (ExceptionCode == 0x406d1388) {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ /*
+ * Ignore .NET exceptions.
+ *
+ * http://ig2600.blogspot.co.uk/2011/01/why-do-i-keep-getting-exception-code.html
+ * https://github.com/dotnet/coreclr/blob/master/src/jit/importer.cpp
+ */
+ if (ExceptionCode == 0xe0434352 ||
+ ExceptionCode == 0xe0564552) {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ log("apitrace: warning: caught exception 0x%08lx\n", pExceptionRecord->ExceptionCode);
+
+ static int recursion_count = 0;
+ if (recursion_count) {
+ fputs("apitrace: warning: recursion handling exception\n", stderr);
+ } else {
+ if (gCallback) {
+ ++recursion_count;
+ gCallback();
+ --recursion_count;
+ }
+ }
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+void
+setExceptionCallback(void (*callback)(void))
+{
+ assert(!gCallback);
+
+ if (!gCallback) {
+ gCallback = callback;
+
+ assert(!prevExceptionFilter);
+
+ prevExceptionFilter = AddVectoredExceptionHandler(0, unhandledExceptionHandler);
+ }
+}
+
+void
+resetExceptionCallback(void)
+{
+ if (gCallback) {
+ RemoveVectoredExceptionHandler(prevExceptionFilter);
+ gCallback = NULL;
+ }
+}
+
+
+} /* namespace os */
+
+#endif // defined(_WIN32)
diff --git a/lib/os/thread_pool.hpp b/lib/os/thread_pool.hpp
new file mode 100644
index 00000000..9ccba267
--- /dev/null
+++ b/lib/os/thread_pool.hpp
@@ -0,0 +1,113 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2012 Jakob Progsch, Václav Zeman
+ * Copyright (c) 2015 Emmanuel Gil Peyrot, Collabora Ltd.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ **************************************************************************/
+
+
+#pragma once
+
+#include <algorithm>
+#include <cstddef>
+#include <functional>
+#include <queue>
+#include <vector>
+
+#include "os_thread.hpp"
+
+
+class ThreadPool {
+public:
+ ThreadPool(size_t);
+ template<class F, class... Args>
+ void enqueue(F&& f, Args&&... args);
+ ~ThreadPool();
+private:
+ // need to keep track of threads so we can join them
+ std::vector<os::thread> workers;
+ // the task queue
+ std::queue<std::function<void()>> tasks;
+
+ // synchronization
+ os::mutex queue_mutex;
+ os::condition_variable condition;
+ bool stop;
+};
+
+
+// the constructor just launches some amount of workers
+inline ThreadPool::ThreadPool(size_t threads)
+ : stop(false)
+{
+ for(size_t i = 0;i<threads;++i)
+ workers.emplace_back(
+ [this]
+ {
+ for(;;)
+ {
+ std::function<void()> task;
+
+ {
+ os::unique_lock<os::mutex> lock(this->queue_mutex);
+ this->condition.wait(lock,
+ [this]{ return this->stop || !this->tasks.empty(); });
+ if(this->stop && this->tasks.empty())
+ return;
+ task = std::move(this->tasks.front());
+ this->tasks.pop();
+ }
+
+ task();
+ }
+ }
+ );
+}
+
+// add new work item to the pool
+template<class F, class... Args>
+void ThreadPool::enqueue(F&& f, Args&&... args)
+{
+ auto task = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
+
+ {
+ os::unique_lock<os::mutex> lock(queue_mutex);
+
+ // don't allow enqueueing after stopping the pool
+ assert(!stop);
+
+ tasks.emplace(task);
+ }
+ condition.notify_one();
+}
+
+// the destructor joins all threads
+inline ThreadPool::~ThreadPool()
+{
+ {
+ os::unique_lock<os::mutex> lock(queue_mutex);
+ stop = true;
+ }
+ condition.notify_all();
+ for(os::thread &worker: workers)
+ worker.join();
+}