<commit_before>#include "libpstack/dwarf.h"
#include "libpstack/flags.h"
#include "libpstack/global.h"
#include "libpstack/proc.h"
#include "libpstack/fs.h"
#include "libpstack/ps_callback.h"
#if defined(WITH_PYTHON2) || defined(WITH_PYTHON3)
#define WITH_PYTHON
#include "libpstack/python.h"
#endif

#include <sys/types.h>
#include <sys/signal.h>
#include <sys/wait.h>

#include <sysexits.h>
#include <unistd.h>

#include <csignal>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <set>

#define XSTR(a) #a
#define STR(a) XSTR(a)
extern std::ostream & operator << (std::ostream &os, const JSON<ThreadStack, Process *> &jt);

namespace {
bool doJson = false;
volatile bool interrupted = false;

void
pstack(Process &proc, const PstackOptions &options, int maxFrames)
{
    const auto &threadStacks = proc.getStacks(options, maxFrames);
    auto &os = *options.output;
    if (doJson) {
        os << json(threadStacks, &proc);
    } else {
        os << "process: " << *proc.io << "\n";
        for (auto &s : threadStacks) {
            proc.dumpStackText(os, s, options);
            os << std::endl;
        }
    }
}

int
startChild(Elf::Object::sptr exe, const std::string &cmd, const PstackOptions &options, Dwarf::ImageCache &ic) {
   std::vector<std::string> args;
   for (size_t off = 0;;) {
      auto pos = cmd.find(' ', off);
      if (pos == std::string::npos) {
         args.push_back(cmd.substr(off));
         break;
      } else {
         args.push_back(cmd.substr(off, pos));
         off = pos + 1;
      }
   }

   int rc, status;
   pid_t pid = fork();
   switch (pid) {
      case 0: {
         rc = ptrace(PTRACE_TRACEME, 0, 0, 0);
         assert(rc == 0);
         if (verbose > 2)
             *debug << getpid() << " traced, waiting for debugger..." << std::endl;
         raise(SIGSTOP);
         if (verbose > 2)
             *debug << getpid() << " ... resumed\n";
         std::vector<const char *> sysargs;
         std::transform(args.begin(), args.end(), std::back_inserter(sysargs),
                        [] (const std::string &arg) { return arg.c_str(); });
         sysargs.push_back(nullptr);
         execvp(sysargs[0], (char **)&sysargs[0]);
         if (verbose > 2)
             *debug << getpid() << " execvp failed: " << strerror(errno) << "\n";
         // child
         break;
      }
      case -1:
         // error
         return -1;
      default:

         std::shared_ptr<Process> p;
         while (true) {
            if (verbose > 2)
               *debug << getpid() << " waiting...\n";
            rc = wait(&status);
            if (rc != pid) {
               if (verbose > 2)
                  *debug << getpid() << "... wait failed: " << strerror(errno) << "\n";
               break;
            }
            if (verbose > 2)
               *debug << getpid() << "... done - rc=" << rc << ", status=" << WaitStatus(status) << "\n";

            if (WIFSTOPPED(status)) {
               int contsig = WSTOPSIG(status);
               switch (contsig) {
                  case SIGSTOP:
                     contsig = 0;
                     break;
                  case SIGTRAP:
                     contsig = 0;
                     break;
                  default:
                     if (p == nullptr) {
                        p = std::make_shared<LiveProcess>(exe, pid, options, ic, true);
                        p->load();
                     }
                     pstack(*p, std::cout, options, 100);
               }
               rc = ptrace(PTRACE_CONT, pid, 0, contsig == SIGTRAP ? 0 : contsig);
               if (rc == -1)
                  *debug << getpid() << " ptrace failed to continue - " << strerror(errno) << "\n";

               assert(rc == 0);
               if (verbose > 2)
                  *debug << getpid() << "..done\n";
            } else {
               return 0;
            }
         }
         break;
   }
   return 0;
}

#ifdef WITH_PYTHON
template<int V> void doPy(Process &proc, const PstackOptions &options, bool showModules, const PyInterpInfo &info) {
    StopProcess here(&proc);
    PythonPrinter<V> printer(proc, *options.output, options, info);
    if (!printer.interpFound())
        throw Exception() << "no python interpreter found";
    printer.printInterpreters(showModules);
}

/**
 * @brief Given a process, tries to print the Python strack trace of it.
 * If the process wasn't a Python process, returns false.
 * True on successful printing of Python stack trace
 *
 * @param proc          The process
 * @param o             The stream to which to print the otutput
 * @param options       Options
 * @param showModules   Whether to show modules
 * @return              boolean of whether the process was a Python process or not
 */
bool pystack(Process &proc, const PstackOptions &options, bool showModules) {
    PyInterpInfo info = getPyInterpInfo(proc);

    if (info.libpython == nullptr) // not a python process or python interpreter not found
        return false;

    if (info.versionHex < V2HEX(3, 0)) { // Python 2.x
#ifdef WITH_PYTHON2
        doPy<2>(proc, options, showModules, info);
#else
        throw (Exception() << "no support for discovered python 2 interpreter");
#endif
    } else { // Python 3.x
#ifdef WITH_PYTHON3
        doPy<3>(proc, options, showModules, info);
#else
        throw (Exception() << "no support for discovered python 3 interpreter");
#endif
    }
    return true;
}
#endif

int
usage(std::ostream &os, const char *name, const Flags &options)
{
     os <<
"usage: " << name << " <[ exe ] <PID | core> >+\n"
"\n"
"print a stack trace of PID or core. If specified, assume image was created from\n"
" execing `exe`, otherwise, the executable is inferred from the process or core\n"
"\n"
"available options:\n" << options <<  "\n";
     return EX_USAGE;
}

int
emain(int argc, char **argv, Dwarf::ImageCache &imageCache)
{
    int maxFrames = 1024;
    double sleepTime = 0.0;
    PstackOptions options;
    std::ofstream out;

#if defined(WITH_PYTHON)
    bool doPython = false;
    bool pythonModules = false;
#endif
    std::vector<std::string> btLogs;
    std::string execName;
    bool printAllStacks = false;
    int exitCode = -1; // used for options that exit immediately to signal exit.
    std::string subprocessCmd;

    Flags flags;
    flags
    .add("replace-path",
            'F',
            "from:to",
            "replace `from` with `to` in paths when finding shared libraries",
            [&](const char *arg) {
                auto sep = strchr(arg, ':');
                if (sep == 0)
                    usage(std::cerr, argv[0], flags);
                pathReplacements.push_back(std::make_pair(
                            std::string(arg, sep - arg), std::string(sep + 1))); })

    .add("debug-dir",
            'g',
            "directory",
            "extra location to find debug files for binaries and shared libraries",
            [&](const char *arg) { Elf::globalDebugDirectories.push_back(arg); })

    .add("constant",
            'b',
            "delay",
            "repeat pstack, with `delay` seconds between each iteration (can be non-integer)",
            Flags::set(sleepTime))

    .add("elf-dump",
            'd',
            "ELF file",
            "dump details of an ELF image in JSON and exit",
            [&](const char *arg) {
                *options.output << json(Elf::Object(imageCache, loadFile(arg)));
                exitCode = 0; })

    .add("dwarf-dump",
            'D',
            "ELF file",
            "dump details of DWARF information in an ELF image in JSON and exit",
            [&](const char *arg) {
                auto dumpobj = std::make_shared<Elf::Object>(imageCache, loadFile(arg));
                auto di = std::make_shared<Dwarf::Info>(dumpobj, imageCache);
                *options.output << json(*di);
                exitCode = 0; })

    .add("depth",
            'r',
            "depth",
            "max depth when printing python structures",
            Flags::set(options.maxdepth))

    .add("max-frames",
            'M',
            "max frames",
            "maximum number of stack frames to print for a thread",
            Flags::set(maxFrames))

    .add("help",
            'h',
            "generate this help message",
            [&]() { exitCode = usage(std::cout, argv[0], flags); })

    .add("args",
            'a',
            "attempt to show the value of arguments to functions",
            Flags::setf(options.doargs))

    .add("json",
            'j',
            "use JSON output rather than plaintext",
            Flags::setf(doJson))

    .add("no-src",
            's',
            "don't include source info",
            Flags::setf(options.nosrc))

    .add("verbose",
            'v',
            "more debugging data. Can be repeated",
            [&]() { ++verbose; })

    .add("no-threaddb",
            't',
            "don't use the thread_db functions to enumerate pthreads (just uses LWPs)",
            Flags::setf(options.nothreaddb))

    .add("all",
            'A',
            "show both python and DWARF (C/C++/go/rust) stack traces",
            Flags::setf(printAllStacks))

    .add("no-ext-debug",
            'n',
            "don't load external debugging information when processing",
            Flags::setf(Elf::noExtDebug))

    .add("version",
            'V',
            "dump version and exit",
            [&]() {
               std::clog << STR(VERSION) << "\n";
               exitCode = 0; })

#ifdef WITH_PYTHON
    .add("python-modules",
            'm',
            "print contents of all python modules when tracing",
            Flags::setf(pythonModules))

    .add("python",
            'p',
            "print python stack traces",
            Flags::setf(doPython))

    .add("locals",
            'l',
            "print local variables (just python for now)",
            Flags::setf(options.dolocals))
#endif
    .add("from-log",
            'L',
            "log-file",
            "print stack trace given log file including instruction pointers",
            [&](const char *log) {
               btLogs.push_back(log);
            })
    .add("executable",
          'e',
          "executable",
          "executable to use by default", [&](const char *opt) { execName = opt; })
    .add("command",
          'x',
          "command line",
          "execute command line as subprocess", Flags::set<std::string>(subprocessCmd))
    .add("output",
          'o',
          "output file",
          "write output to <output file> instead of stdout", [&options, &out] (const char *opt) {
             out = std::move(std::ofstream(opt, std::ofstream::out|std::ofstream::trunc));
             options.output = &out;
          })
    .parse(argc, argv);

    if (exitCode != -1)
        return exitCode;

    // any instance of a non-core ELF image will override default behaviour of
    // discovering the executable
    Elf::Object::sptr exec;
    if (execName != "")
         exec = imageCache.getImageForName(execName);

    if (subprocessCmd != "") {
        // create a new process and trace it.
        startChild(exec, subprocessCmd, options, imageCache);
        return 0;
    }

    if (optind == argc && btLogs.empty())
        return usage(std::cerr, argv[0], flags);

    auto doStack = [=, &options] (Process &proc) {
        proc.load();
        while (!interrupted) {
#if defined(WITH_PYTHON)
            if (doPython || printAllStacks) {
                bool isPythonProcess = pystack(proc, options, pythonModules);
                // error if -p but not python process
                if (doPython && !isPythonProcess)
                    throw Exception() << "Couldn't find a Python interpreter";
            }
            if (!doPython)
#endif
            {
                pstack(proc, options, maxFrames);
            }
            if (sleepTime != 0.0)
                usleep(sleepTime * 1000000);
            else
                break;
        }
    };

    if (!btLogs.empty()) {
       LogProcess lp{exec, btLogs, options, imageCache};
       doStack(lp);
       return 0;
    } else {
        for (int i = optind; i < argc; i++) {
            try {
                auto process = Process::load(exec, argv[i], options, imageCache);
                if (process == nullptr)
                    exec = imageCache.getImageForName(argv[i]);
                else
                    doStack(*process);
            } catch (const std::exception &e) {
                std::cerr << "trace of " << argv[i] << " failed: " << e.what() << "\n";
            }
        }
    }
    return 0;
}
}

int
main(int argc, char **argv)
{
    try {
        Dwarf::ImageCache imageCache;
        struct sigaction sa;
        memset(&sa, 0, sizeof sa);
        sa.sa_handler = [](int) { interrupted = true; };
        // Only interrupt cleanly once. Then just terminate, in case we're stuck in a loop
        sa.sa_flags = SA_RESETHAND;
        sigaction(SIGINT, &sa, nullptr);
        emain(argc, argv, imageCache);
        exit(0);
    }
    catch (std::exception &ex) {
        std::clog << "error: " << ex.what() << std::endl;
        return EX_SOFTWARE;
    }
}
<commit_msg>Fix up startChild to be useful for tests<commit_after>#include "libpstack/dwarf.h"
#include "libpstack/flags.h"
#include "libpstack/global.h"
#include "libpstack/proc.h"
#include "libpstack/fs.h"
#include "libpstack/ps_callback.h"
#if defined(WITH_PYTHON2) || defined(WITH_PYTHON3)
#define WITH_PYTHON
#include "libpstack/python.h"
#endif

#include <sys/types.h>
#include <sys/signal.h>
#include <sys/wait.h>

#include <sysexits.h>
#include <unistd.h>

#include <csignal>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <set>

#define XSTR(a) #a
#define STR(a) XSTR(a)
extern std::ostream & operator << (std::ostream &os, const JSON<ThreadStack, Process *> &jt);

namespace {
bool doJson = false;
volatile bool interrupted = false;

void
pstack(Process &proc, const PstackOptions &options, int maxFrames)
{
    const auto &threadStacks = proc.getStacks(options, maxFrames);
    auto &os = *options.output;
    if (doJson) {
        os << json(threadStacks, &proc);
    } else {
        os << "process: " << *proc.io << "\n";
        for (auto &s : threadStacks) {
            proc.dumpStackText(os, s, options);
            os << std::endl;
        }
    }
}

// This is mostly for testing. We start the process, and run pstack when we see
// a signal that is likely to terminate the process, then kill it. This allows
// us to reliably run pstack on a process that will abort or segfault, and
// doesn't require a readable core file.
int
startChild(Elf::Object::sptr exe, const std::string &cmd, const PstackOptions &options, Dwarf::ImageCache &ic) {
   std::vector<std::string> args;
   for (size_t off = 0;;) {
      auto pos = cmd.find(' ', off);
      if (pos == std::string::npos) {
         args.push_back(cmd.substr(off));
         break;
      } else {
         args.push_back(cmd.substr(off, pos));
         off = pos + 1;
      }
   }

   int rc, status;
   pid_t pid = fork();
   switch (pid) {
      case 0: {
         rc = ptrace(PTRACE_TRACEME, 0, 0, 0);
         assert(rc == 0);
         std::vector<const char *> sysargs;
         std::transform(args.begin(), args.end(), std::back_inserter(sysargs),
                        [] (const std::string &arg) { return arg.c_str(); });
         sysargs.push_back(nullptr);
         execvp(sysargs[0], (char **)&sysargs[0]);
         if (verbose > 2)
             *debug << getpid() << " execvp failed: " << strerror(errno) << "\n";
         // child
         break;
      }
      case -1:
         // error
         return -1;
      default:
         std::shared_ptr<Process> p;
         char statusBuf[PATH_MAX];
         snprintf(statusBuf, sizeof statusBuf, "/proc/%d/status", pid);
         struct closer { void operator()(FILE *f){ fclose(f); }};
         std::unique_ptr<FILE, closer> statusFile { fopen(statusBuf, "r") };
         assert(statusFile.get());

         for (;;) {
            if (verbose > 2)
               *debug << getpid() << " waiting...\n";
            rc = wait(&status);
            if (rc != pid) {
               if (verbose > 2)
                  *debug << getpid() << "... wait failed: " << strerror(errno) << "\n";
               break;
            }
            if (verbose > 2)
               *debug << getpid() << "... done - rc=" << rc << ", status=" << WaitStatus(status) << "\n";

            if (WIFSTOPPED(status)) {
               // Read the content of the process's SigIgn and SigCgt info from procfs.
               fflush(statusFile.get());
               fseek(statusFile.get(), 0, SEEK_SET);
               char line[PATH_MAX];
               unsigned long sigblk = -1, sigign = -1, sigcgt = -1;
               while (fgets(line, sizeof line, statusFile.get()) != NULL) {
                  if (strncmp(line, "SigBlk:\t", 8) == 0)
                     sigblk = strtoul(line + 8, 0, 16);
                  else if (strncmp(line, "SigCgt:\t", 8) == 0)
                     sigcgt = strtoul(line + 8, 0, 16);
                  else if (strncmp(line, "SigIgn:\t", 8) == 0)
                     sigign = strtoul(line + 8, 0, 16);
               }
               unsigned long handledSigs = sigblk | sigcgt | sigign;
               handledSigs |= 1 << (SIGTRAP - 1);
               int stopsig = WSTOPSIG(status);
               int contsig = stopsig == SIGSTOP || stopsig == SIGTRAP ? 0 : stopsig;
               if (((1 << (stopsig -1)) & handledSigs) == 0) {
                  p = std::make_shared<LiveProcess>(exe, pid, options, ic, true);
                  p->load();
                  pstack(*p, options, 100);
                  rc = ptrace(PTRACE_KILL, pid, 0, contsig);
               } else {
                  rc = ptrace(PTRACE_CONT, pid, 0, contsig);
               }
               if (rc == -1)
                  *debug << getpid() << " ptrace failed to kill/continue - " << strerror(errno) << "\n";
               assert(rc == 0);
               if (verbose > 2)
                  *debug << getpid() << "..done\n";
            }
            else {
               return 0;
            }
         }
         break;
   }
   return 0;
}

#ifdef WITH_PYTHON
template<int V> void doPy(Process &proc, const PstackOptions &options, bool showModules, const PyInterpInfo &info) {
    StopProcess here(&proc);
    PythonPrinter<V> printer(proc, *options.output, options, info);
    if (!printer.interpFound())
        throw Exception() << "no python interpreter found";
    printer.printInterpreters(showModules);
}

/**
 * @brief Given a process, tries to print the Python strack trace of it.
 * If the process wasn't a Python process, returns false.
 * True on successful printing of Python stack trace
 *
 * @param proc          The process
 * @param o             The stream to which to print the otutput
 * @param options       Options
 * @param showModules   Whether to show modules
 * @return              boolean of whether the process was a Python process or not
 */
bool pystack(Process &proc, const PstackOptions &options, bool showModules) {
    PyInterpInfo info = getPyInterpInfo(proc);

    if (info.libpython == nullptr) // not a python process or python interpreter not found
        return false;

    if (info.versionHex < V2HEX(3, 0)) { // Python 2.x
#ifdef WITH_PYTHON2
        doPy<2>(proc, options, showModules, info);
#else
        throw (Exception() << "no support for discovered python 2 interpreter");
#endif
    } else { // Python 3.x
#ifdef WITH_PYTHON3
        doPy<3>(proc, options, showModules, info);
#else
        throw (Exception() << "no support for discovered python 3 interpreter");
#endif
    }
    return true;
}
#endif

int
usage(std::ostream &os, const char *name, const Flags &options)
{
     os <<
"usage: " << name << " <[ exe ] <PID | core> >+\n"
"\n"
"print a stack trace of PID or core. If specified, assume image was created from\n"
" execing `exe`, otherwise, the executable is inferred from the process or core\n"
"\n"
"available options:\n" << options <<  "\n";
     return EX_USAGE;
}

int
emain(int argc, char **argv, Dwarf::ImageCache &imageCache)
{
    int maxFrames = 1024;
    double sleepTime = 0.0;
    PstackOptions options;
    std::ofstream out;

#if defined(WITH_PYTHON)
    bool doPython = false;
    bool pythonModules = false;
#endif
    std::vector<std::string> btLogs;
    std::string execName;
    bool printAllStacks = false;
    int exitCode = -1; // used for options that exit immediately to signal exit.
    std::string subprocessCmd;

    Flags flags;
    flags
    .add("replace-path",
            'F',
            "from:to",
            "replace `from` with `to` in paths when finding shared libraries",
            [&](const char *arg) {
                auto sep = strchr(arg, ':');
                if (sep == 0)
                    usage(std::cerr, argv[0], flags);
                pathReplacements.push_back(std::make_pair(
                            std::string(arg, sep - arg), std::string(sep + 1))); })

    .add("debug-dir",
            'g',
            "directory",
            "extra location to find debug files for binaries and shared libraries",
            [&](const char *arg) { Elf::globalDebugDirectories.push_back(arg); })

    .add("constant",
            'b',
            "delay",
            "repeat pstack, with `delay` seconds between each iteration (can be non-integer)",
            Flags::set(sleepTime))

    .add("elf-dump",
            'd',
            "ELF file",
            "dump details of an ELF image in JSON and exit",
            [&](const char *arg) {
                *options.output << json(Elf::Object(imageCache, loadFile(arg)));
                exitCode = 0; })

    .add("dwarf-dump",
            'D',
            "ELF file",
            "dump details of DWARF information in an ELF image in JSON and exit",
            [&](const char *arg) {
                auto dumpobj = std::make_shared<Elf::Object>(imageCache, loadFile(arg));
                auto di = std::make_shared<Dwarf::Info>(dumpobj, imageCache);
                *options.output << json(*di);
                exitCode = 0; })

    .add("depth",
            'r',
            "depth",
            "max depth when printing python structures",
            Flags::set(options.maxdepth))

    .add("max-frames",
            'M',
            "max frames",
            "maximum number of stack frames to print for a thread",
            Flags::set(maxFrames))

    .add("help",
            'h',
            "generate this help message",
            [&]() { exitCode = usage(std::cout, argv[0], flags); })

    .add("args",
            'a',
            "attempt to show the value of arguments to functions",
            Flags::setf(options.doargs))

    .add("json",
            'j',
            "use JSON output rather than plaintext",
            Flags::setf(doJson))

    .add("no-src",
            's',
            "don't include source info",
            Flags::setf(options.nosrc))

    .add("verbose",
            'v',
            "more debugging data. Can be repeated",
            [&]() { ++verbose; })

    .add("no-threaddb",
            't',
            "don't use the thread_db functions to enumerate pthreads (just uses LWPs)",
            Flags::setf(options.nothreaddb))

    .add("all",
            'A',
            "show both python and DWARF (C/C++/go/rust) stack traces",
            Flags::setf(printAllStacks))

    .add("no-ext-debug",
            'n',
            "don't load external debugging information when processing",
            Flags::setf(Elf::noExtDebug))

    .add("version",
            'V',
            "dump version and exit",
            [&]() {
               std::clog << STR(VERSION) << "\n";
               exitCode = 0; })

#ifdef WITH_PYTHON
    .add("python-modules",
            'm',
            "print contents of all python modules when tracing",
            Flags::setf(pythonModules))

    .add("python",
            'p',
            "print python stack traces",
            Flags::setf(doPython))

    .add("locals",
            'l',
            "print local variables (just python for now)",
            Flags::setf(options.dolocals))
#endif
    .add("from-log",
            'L',
            "log-file",
            "print stack trace given log file including instruction pointers",
            [&](const char *log) {
               btLogs.push_back(log);
            })
    .add("executable",
          'e',
          "executable",
          "executable to use by default", [&](const char *opt) { execName = opt; })
    .add("command",
          'x',
          "command line",
          "execute command line as subprocess, trace when it receives a signal", Flags::set<std::string>(subprocessCmd))
    .add("output",
          'o',
          "output file",
          "write output to <output file> instead of stdout", [&options, &out] (const char *opt) {
             out = std::move(std::ofstream(opt, std::ofstream::out|std::ofstream::trunc));
             options.output = &out;
          })
    .parse(argc, argv);

    if (exitCode != -1)
        return exitCode;

    // any instance of a non-core ELF image will override default behaviour of
    // discovering the executable
    Elf::Object::sptr exec;
    if (execName != "")
         exec = imageCache.getImageForName(execName);

    if (subprocessCmd != "") {
        // create a new process and trace it.
        startChild(exec, subprocessCmd, options, imageCache);
        return 0;
    }

    if (optind == argc && btLogs.empty())
        return usage(std::cerr, argv[0], flags);

    auto doStack = [=, &options] (Process &proc) {
        proc.load();
        while (!interrupted) {
#if defined(WITH_PYTHON)
            if (doPython || printAllStacks) {
                bool isPythonProcess = pystack(proc, options, pythonModules);
                // error if -p but not python process
                if (doPython && !isPythonProcess)
                    throw Exception() << "Couldn't find a Python interpreter";
            }
            if (!doPython)
#endif
            {
                pstack(proc, options, maxFrames);
            }
            if (sleepTime != 0.0)
                usleep(sleepTime * 1000000);
            else
                break;
        }
    };
    if (!btLogs.empty()) {
       LogProcess lp{exec, btLogs, options, imageCache};
       doStack(lp);
    } else {
        for (int i = optind; i < argc; i++) {
            try {
                auto process = Process::load(exec, argv[i], options, imageCache);
                if (process == nullptr)
                    exec = imageCache.getImageForName(argv[i]);
                else
                    doStack(*process);
            } catch (const std::exception &e) {
                std::cerr << "trace of " << argv[i] << " failed: " << e.what() << "\n";
            }
        }
    }
    return 0;
}
}

int
main(int argc, char **argv)
{
    try {
        Dwarf::ImageCache imageCache;
        struct sigaction sa;
        memset(&sa, 0, sizeof sa);
        sa.sa_handler = [](int) { interrupted = true; };
        // Only interrupt cleanly once. Then just terminate, in case we're stuck in a loop
        sa.sa_flags = SA_RESETHAND;
        sigaction(SIGINT, &sa, nullptr);
        emain(argc, argv, imageCache);
        exit(0);
    }
    catch (std::exception &ex) {
        std::clog << "error: " << ex.what() << std::endl;
        return EX_SOFTWARE;
    }
}
<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* bzflag special common - 1st one */
#include "common.h"

#include <iostream>

#include "ActionBinding.h"
#include "CommandManager.h"
#include "KeyManager.h"

// initialize the singleton
template <>
ActionBinding* Singleton<ActionBinding>::_instance = (ActionBinding*)0;

ActionBinding::ActionBinding() {
  wayToBindActions.insert(std::make_pair(std::string("quit"), press));
  wayToBindActions.insert(std::make_pair(std::string("fire"), both));
  wayToBindActions.insert(std::make_pair(std::string("drop"), press));
  wayToBindActions.insert(std::make_pair(std::string("identify"), press));
  wayToBindActions.insert(std::make_pair(std::string("jump"), press));
  wayToBindActions.insert(std::make_pair(std::string("send all"), press));
  wayToBindActions.insert(std::make_pair(std::string("send team"), press));
  wayToBindActions.insert(std::make_pair(std::string("send nemesis"), press));
  wayToBindActions.insert(std::make_pair(std::string("send recipient"), press));
  wayToBindActions.insert(std::make_pair(std::string("send admin"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle displayScore"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle displayBinoculars"), press));
  wayToBindActions.insert(std::make_pair(std::string("pause"), press));
  wayToBindActions.insert(std::make_pair(std::string("fullscreen"), press));
  wayToBindActions.insert(std::make_pair(std::string("iconify"), press));
#ifdef SNAPPING
  wayToBindActions.insert(std::make_pair(std::string("screenshot"), press));
#endif
  wayToBindActions.insert(std::make_pair(std::string("time backward"), press));
  wayToBindActions.insert(std::make_pair(std::string("time forward"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggleFlags radar"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggleFlags main"), press));
  wayToBindActions.insert(std::make_pair(std::string("silence"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle displayLabels"), press));
  wayToBindActions.insert(std::make_pair(std::string("destruct"), press));

  // Movement keys
  wayToBindActions.insert(std::make_pair(std::string("turn left"), both));
  wayToBindActions.insert(std::make_pair(std::string("turn right"), both));
  wayToBindActions.insert(std::make_pair(std::string("drive forward"), both));
  wayToBindActions.insert(std::make_pair(std::string("drive reverse"), both));
  // End movement keys

  wayToBindActions.insert(std::make_pair(std::string("roam cycle subject backward"),
					 press));
  wayToBindActions.insert(std::make_pair(std::string("roam cycle subject forward"), press));
  wayToBindActions.insert(std::make_pair(std::string("roam cycle type forward"), press));
  wayToBindActions.insert(std::make_pair(std::string("roam zoom in"), both));
  wayToBindActions.insert(std::make_pair(std::string("roam zoom out"), both));
  wayToBindActions.insert(std::make_pair(std::string("roam zoom normal"), both));
  wayToBindActions.insert(std::make_pair(std::string("servercommand"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle displayFlagHelp"), press));
  wayToBindActions.insert(std::make_pair(std::string("scrollpanel up"), press));
  wayToBindActions.insert(std::make_pair(std::string("scrollpanel down"), press));
  wayToBindActions.insert(std::make_pair(std::string("set displayRadarRange 0.25"), press));
  wayToBindActions.insert(std::make_pair(std::string("set displayRadarRange 0.5"), press));
  wayToBindActions.insert(std::make_pair(std::string("set displayRadarRange 1.0"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle slowKeyboard"), press));
  wayToBindActions.insert(std::make_pair(std::string("hunt"), press));
  wayToBindActions.insert(std::make_pair(std::string("restart"), release));
  wayToBindActions.insert(std::make_pair(std::string("autopilot"), press));

  defaultBinding.insert(std::make_pair(std::string("F12"), std::string("quit")));
  defaultBinding.insert(std::make_pair(std::string("Left Mouse"), std::string("fire")));
  defaultBinding.insert(std::make_pair(std::string("Enter"), std::string("fire")));
  defaultBinding.insert(std::make_pair(std::string("Middle Mouse"), std::string("drop")));
  defaultBinding.insert(std::make_pair(std::string("Space"), std::string("drop")));
  defaultBinding.insert(std::make_pair(std::string("Right Mouse"), std::string("identify")));
  defaultBinding.insert(std::make_pair(std::string("I"), std::string("identify")));
  defaultBinding.insert(std::make_pair(std::string("Tab"), std::string("jump")));
  defaultBinding.insert(std::make_pair(std::string("N"), std::string("send all")));
  defaultBinding.insert(std::make_pair(std::string("M"), std::string("send team")));
  defaultBinding.insert(std::make_pair(std::string(","), std::string("send nemesis")));
  defaultBinding.insert(std::make_pair(std::string("."), std::string("send recipient")));
  defaultBinding.insert(std::make_pair(std::string("Z"), std::string("send admin")));
  defaultBinding.insert(std::make_pair(std::string("S"), std::string("toggle displayScore")));
  defaultBinding.insert(std::make_pair(std::string("B"), std::string("toggle displayBinoculars")));
  defaultBinding.insert(std::make_pair(std::string("Pause"), std::string("pause")));
  defaultBinding.insert(std::make_pair(std::string("P"), std::string("pause")));
  defaultBinding.insert(std::make_pair(std::string("F1"),
				       std::string("fullscreen")));
  defaultBinding.insert(std::make_pair(std::string("F4"), std::string("iconify")));
#ifdef SNAPPING
  defaultBinding.insert(std::make_pair(std::string("F5"), std::string("screenshot")));
#endif
  defaultBinding.insert(std::make_pair(std::string("-"), std::string("time backward")));
  defaultBinding.insert(std::make_pair(std::string("="), std::string("time forward")));
  defaultBinding.insert(std::make_pair(std::string("H"), std::string("toggleFlags radar")));
  defaultBinding.insert(std::make_pair(std::string("J"), std::string("toggleFlags main")));
  defaultBinding.insert(std::make_pair(std::string("K"), std::string("silence")));
  defaultBinding.insert(std::make_pair(std::string("L"), std::string("toggle displayLabels")));
  defaultBinding.insert(std::make_pair(std::string("Delete"), std::string("destruct")));

  // Default movement keys
  defaultBinding.insert(std::make_pair(std::string("Left Arrow"),
				       std::string("turn left")));
  defaultBinding.insert(std::make_pair(std::string("Right Arrow"),
				       std::string("turn right")));
  defaultBinding.insert(std::make_pair(std::string("Up Arrow"),
				       std::string("drive forward")));
  defaultBinding.insert(std::make_pair(std::string("Down Arrow"),
				       std::string("drive reverse")));
  // End default movement keys

  defaultBinding.insert(std::make_pair(std::string("F6"), std::string("roam cycle subject backward")));
  defaultBinding.insert(std::make_pair(std::string("F7"), std::string("roam cycle subject forward")));
  defaultBinding.insert(std::make_pair(std::string("F8"), std::string("roam cycle type forward")));
  defaultBinding.insert(std::make_pair(std::string("F9"), std::string("roam zoom in")));
  defaultBinding.insert(std::make_pair(std::string("F10"), std::string("roam zoom out")));
  defaultBinding.insert(std::make_pair(std::string("F11"), std::string("roam zoom normal")));
  defaultBinding.insert(std::make_pair(std::string("O"), std::string("servercommand")));
  defaultBinding.insert(std::make_pair(std::string("F"), std::string("toggle displayFlagHelp")));
  defaultBinding.insert(std::make_pair(std::string("Page Up"), std::string("scrollpanel up")));
  defaultBinding.insert(std::make_pair(std::string("Page Down"), std::string("scrollpanel down")));
  defaultBinding.insert(std::make_pair(std::string("1"), std::string("set displayRadarRange 0.25")));
  defaultBinding.insert(std::make_pair(std::string("2"), std::string("set displayRadarRange 0.5")));
  defaultBinding.insert(std::make_pair(std::string("3"), std::string("set displayRadarRange 1.0")));
  defaultBinding.insert(std::make_pair(std::string("A"), std::string("toggle slowKeyboard")));
  defaultBinding.insert(std::make_pair(std::string("U"), std::string("hunt")));
  defaultBinding.insert(std::make_pair(std::string("Right Mouse"), std::string("restart")));
  defaultBinding.insert(std::make_pair(std::string("I"), std::string("restart")));
  defaultBinding.insert(std::make_pair(std::string("9"), std::string("autopilot")));
}

void ActionBinding::resetBindings() {
  BindingTable::const_iterator index;

  for (index = bindingTable.begin();
       index != bindingTable.end();
       ++index)
    unbind(index->second, index->first);

  bindingTable = defaultBinding;

  for (index = bindingTable.begin();
       index != bindingTable.end();
       ++index)
    bind(index->second, index->first);
}

void ActionBinding::getFromBindings() {
  bindingTable.clear();
  KEYMGR.iterate(&onScanCB, this);
}

void ActionBinding::onScanCB(const std::string& name, bool,
			     const std::string& cmd, void*)
{
  ActionBinding::instance().associate(name, cmd, false);
}

void ActionBinding::associate(std::string key,
			      std::string action,
			      bool        keyBind) {
  BindingTable::iterator index, next;
  if (!wayToBindActions.count(action))
    return;
  PressStatusBind newStatusBind = wayToBindActions[action];
  for (index = bindingTable.lower_bound( key ); index != bindingTable.upper_bound( key ); index = next) {
    next = index;
    ++next;
    if (newStatusBind == both) {
      if (keyBind)
	unbind(index->second, key);
      bindingTable.erase(index);
    } else if (newStatusBind == press) {
      if (wayToBindActions[index->second] != release) {
	if (keyBind)
	  unbind(index->second, key);
	bindingTable.erase(index);
      }
    } else {
      if (wayToBindActions[index->second] != press) {
	if (keyBind)
	  unbind(index->second, key);
	bindingTable.erase(index);
      }
    }
  }
  bindingTable.insert(std::make_pair(key, action));
  if (keyBind)
    bind(action, key);
};

void ActionBinding::deassociate(std::string action) {
  BindingTable::iterator index, next;
  for (index = bindingTable.begin();
       index != bindingTable.end();
       index = next) {
    next = index;
    ++next;
    if (index->second == action) {
      unbind(action, index->first);
      bindingTable.erase(index);
    }
  }
};

void ActionBinding::bind(std::string action, std::string key) {
  PressStatusBind statusBind = wayToBindActions[action];
  std::string command;
  if (statusBind == press || statusBind == both) {
    command = "bind \"" + key + "\" down \"" + action + "\"";
    CMDMGR.run(command);
  };
  if (statusBind == release || statusBind == both) {
    command = "bind \"" + key + "\" up \"" + action + "\"";
    CMDMGR.run(command);
  };
}

void ActionBinding::unbind(std::string action, std::string key) {
  PressStatusBind statusBind = wayToBindActions[action];
  std::string command;
  if (statusBind == press || statusBind == both) {
    command = "unbind \"" + key + "\" down";
    CMDMGR.run(command);
  };
  if (statusBind == release || statusBind == both) {
    command = "unbind \"" + key + "\" up";
    CMDMGR.run(command);
  };
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>Adding Message Panel Selection to Shift-F1 - F4<commit_after>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* bzflag special common - 1st one */
#include "common.h"

#include <iostream>

#include "ActionBinding.h"
#include "CommandManager.h"
#include "KeyManager.h"

// initialize the singleton
template <>
ActionBinding* Singleton<ActionBinding>::_instance = (ActionBinding*)0;

ActionBinding::ActionBinding() {
  wayToBindActions.insert(std::make_pair(std::string("quit"), press));
  wayToBindActions.insert(std::make_pair(std::string("fire"), both));
  wayToBindActions.insert(std::make_pair(std::string("drop"), press));
  wayToBindActions.insert(std::make_pair(std::string("identify"), press));
  wayToBindActions.insert(std::make_pair(std::string("jump"), press));
  wayToBindActions.insert(std::make_pair(std::string("send all"), press));
  wayToBindActions.insert(std::make_pair(std::string("send team"), press));
  wayToBindActions.insert(std::make_pair(std::string("send nemesis"), press));
  wayToBindActions.insert(std::make_pair(std::string("send recipient"), press));
  wayToBindActions.insert(std::make_pair(std::string("send admin"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle displayScore"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle displayBinoculars"), press));
  wayToBindActions.insert(std::make_pair(std::string("pause"), press));
  wayToBindActions.insert(std::make_pair(std::string("fullscreen"), press));
  wayToBindActions.insert(std::make_pair(std::string("iconify"), press));
#ifdef SNAPPING
  wayToBindActions.insert(std::make_pair(std::string("screenshot"), press));
#endif
  wayToBindActions.insert(std::make_pair(std::string("time backward"), press));
  wayToBindActions.insert(std::make_pair(std::string("time forward"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggleFlags radar"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggleFlags main"), press));
  wayToBindActions.insert(std::make_pair(std::string("silence"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle displayLabels"), press));
  wayToBindActions.insert(std::make_pair(std::string("destruct"), press));

  // Movement keys
  wayToBindActions.insert(std::make_pair(std::string("turn left"), both));
  wayToBindActions.insert(std::make_pair(std::string("turn right"), both));
  wayToBindActions.insert(std::make_pair(std::string("drive forward"), both));
  wayToBindActions.insert(std::make_pair(std::string("drive reverse"), both));
  // End movement keys

  wayToBindActions.insert(std::make_pair(std::string("roam cycle subject backward"),
					 press));
  wayToBindActions.insert(std::make_pair(std::string("roam cycle subject forward"), press));
  wayToBindActions.insert(std::make_pair(std::string("roam cycle type forward"), press));
  wayToBindActions.insert(std::make_pair(std::string("roam zoom in"), both));
  wayToBindActions.insert(std::make_pair(std::string("roam zoom out"), both));
  wayToBindActions.insert(std::make_pair(std::string("roam zoom normal"), both));
  wayToBindActions.insert(std::make_pair(std::string("servercommand"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle displayFlagHelp"), press));
  wayToBindActions.insert(std::make_pair(std::string("scrollpanel up"), press));
  wayToBindActions.insert(std::make_pair(std::string("scrollpanel down"), press));
  wayToBindActions.insert(std::make_pair(std::string("set displayRadarRange 0.25"), press));
  wayToBindActions.insert(std::make_pair(std::string("set displayRadarRange 0.5"), press));
  wayToBindActions.insert(std::make_pair(std::string("set displayRadarRange 1.0"), press));
  wayToBindActions.insert(std::make_pair(std::string("toggle slowKeyboard"), press));
  wayToBindActions.insert(std::make_pair(std::string("hunt"), press));
  wayToBindActions.insert(std::make_pair(std::string("restart"), release));
  wayToBindActions.insert(std::make_pair(std::string("autopilot"), press));

  wayToBindActions.insert(std::make_pair(std::string("messagepanel all"),
					 press));
  wayToBindActions.insert(std::make_pair(std::string("messagepanel chat"),
					 press));
  wayToBindActions.insert(std::make_pair(std::string("messagepanel server"),
					 press));
  wayToBindActions.insert(std::make_pair(std::string("messagepanel misc"),
					 press));

  defaultBinding.insert(std::make_pair(std::string("F12"), std::string("quit")));
  defaultBinding.insert(std::make_pair(std::string("Left Mouse"), std::string("fire")));
  defaultBinding.insert(std::make_pair(std::string("Enter"), std::string("fire")));
  defaultBinding.insert(std::make_pair(std::string("Middle Mouse"), std::string("drop")));
  defaultBinding.insert(std::make_pair(std::string("Space"), std::string("drop")));
  defaultBinding.insert(std::make_pair(std::string("Right Mouse"), std::string("identify")));
  defaultBinding.insert(std::make_pair(std::string("I"), std::string("identify")));
  defaultBinding.insert(std::make_pair(std::string("Tab"), std::string("jump")));
  defaultBinding.insert(std::make_pair(std::string("N"), std::string("send all")));
  defaultBinding.insert(std::make_pair(std::string("M"), std::string("send team")));
  defaultBinding.insert(std::make_pair(std::string(","), std::string("send nemesis")));
  defaultBinding.insert(std::make_pair(std::string("."), std::string("send recipient")));
  defaultBinding.insert(std::make_pair(std::string("Z"), std::string("send admin")));
  defaultBinding.insert(std::make_pair(std::string("S"), std::string("toggle displayScore")));
  defaultBinding.insert(std::make_pair(std::string("B"), std::string("toggle displayBinoculars")));
  defaultBinding.insert(std::make_pair(std::string("Pause"), std::string("pause")));
  defaultBinding.insert(std::make_pair(std::string("P"), std::string("pause")));
  defaultBinding.insert(std::make_pair(std::string("F1"),
				       std::string("fullscreen")));
  defaultBinding.insert(std::make_pair(std::string("F4"), std::string("iconify")));
#ifdef SNAPPING
  defaultBinding.insert(std::make_pair(std::string("F5"), std::string("screenshot")));
#endif
  defaultBinding.insert(std::make_pair(std::string("-"), std::string("time backward")));
  defaultBinding.insert(std::make_pair(std::string("="), std::string("time forward")));
  defaultBinding.insert(std::make_pair(std::string("H"), std::string("toggleFlags radar")));
  defaultBinding.insert(std::make_pair(std::string("J"), std::string("toggleFlags main")));
  defaultBinding.insert(std::make_pair(std::string("K"), std::string("silence")));
  defaultBinding.insert(std::make_pair(std::string("L"), std::string("toggle displayLabels")));
  defaultBinding.insert(std::make_pair(std::string("Delete"), std::string("destruct")));

  // Default movement keys
  defaultBinding.insert(std::make_pair(std::string("Left Arrow"),
				       std::string("turn left")));
  defaultBinding.insert(std::make_pair(std::string("Right Arrow"),
				       std::string("turn right")));
  defaultBinding.insert(std::make_pair(std::string("Up Arrow"),
				       std::string("drive forward")));
  defaultBinding.insert(std::make_pair(std::string("Down Arrow"),
				       std::string("drive reverse")));
  // End default movement keys

  defaultBinding.insert(std::make_pair(std::string("F6"), std::string("roam cycle subject backward")));
  defaultBinding.insert(std::make_pair(std::string("F7"), std::string("roam cycle subject forward")));
  defaultBinding.insert(std::make_pair(std::string("F8"), std::string("roam cycle type forward")));
  defaultBinding.insert(std::make_pair(std::string("F9"), std::string("roam zoom in")));
  defaultBinding.insert(std::make_pair(std::string("F10"), std::string("roam zoom out")));
  defaultBinding.insert(std::make_pair(std::string("F11"), std::string("roam zoom normal")));
  defaultBinding.insert(std::make_pair(std::string("O"), std::string("servercommand")));
  defaultBinding.insert(std::make_pair(std::string("F"), std::string("toggle displayFlagHelp")));
  defaultBinding.insert(std::make_pair(std::string("Page Up"), std::string("scrollpanel up")));
  defaultBinding.insert(std::make_pair(std::string("Page Down"), std::string("scrollpanel down")));
  defaultBinding.insert(std::make_pair(std::string("1"), std::string("set displayRadarRange 0.25")));
  defaultBinding.insert(std::make_pair(std::string("2"), std::string("set displayRadarRange 0.5")));
  defaultBinding.insert(std::make_pair(std::string("3"), std::string("set displayRadarRange 1.0")));
  defaultBinding.insert(std::make_pair(std::string("A"), std::string("toggle slowKeyboard")));
  defaultBinding.insert(std::make_pair(std::string("U"), std::string("hunt")));
  defaultBinding.insert(std::make_pair(std::string("Right Mouse"), std::string("restart")));
  defaultBinding.insert(std::make_pair(std::string("I"), std::string("restart")));
  defaultBinding.insert(std::make_pair(std::string("9"), std::string("autopilot")));

  defaultBinding.insert(std::make_pair(std::string("Shift+F1"),
				       std::string("messagepanel all")));
  defaultBinding.insert(std::make_pair(std::string("Shift+F2"),
				       std::string("messagepanel chat")));
  defaultBinding.insert(std::make_pair(std::string("Shift+F3"),
				       std::string("messagepanel server")));
  defaultBinding.insert(std::make_pair(std::string("Shift+F4"),
				       std::string("messagepanel misc")));
}

void ActionBinding::resetBindings() {
  BindingTable::const_iterator index;

  for (index = bindingTable.begin();
       index != bindingTable.end();
       ++index)
    unbind(index->second, index->first);

  bindingTable = defaultBinding;

  for (index = bindingTable.begin();
       index != bindingTable.end();
       ++index)
    bind(index->second, index->first);
}

void ActionBinding::getFromBindings() {
  bindingTable.clear();
  KEYMGR.iterate(&onScanCB, this);
}

void ActionBinding::onScanCB(const std::string& name, bool,
			     const std::string& cmd, void*)
{
  ActionBinding::instance().associate(name, cmd, false);
}

void ActionBinding::associate(std::string key,
			      std::string action,
			      bool        keyBind) {
  BindingTable::iterator index, next;
  if (!wayToBindActions.count(action))
    return;
  PressStatusBind newStatusBind = wayToBindActions[action];
  for (index = bindingTable.lower_bound( key ); index != bindingTable.upper_bound( key ); index = next) {
    next = index;
    ++next;
    if (newStatusBind == both) {
      if (keyBind)
	unbind(index->second, key);
      bindingTable.erase(index);
    } else if (newStatusBind == press) {
      if (wayToBindActions[index->second] != release) {
	if (keyBind)
	  unbind(index->second, key);
	bindingTable.erase(index);
      }
    } else {
      if (wayToBindActions[index->second] != press) {
	if (keyBind)
	  unbind(index->second, key);
	bindingTable.erase(index);
      }
    }
  }
  bindingTable.insert(std::make_pair(key, action));
  if (keyBind)
    bind(action, key);
};

void ActionBinding::deassociate(std::string action) {
  BindingTable::iterator index, next;
  for (index = bindingTable.begin();
       index != bindingTable.end();
       index = next) {
    next = index;
    ++next;
    if (index->second == action) {
      unbind(action, index->first);
      bindingTable.erase(index);
    }
  }
};

void ActionBinding::bind(std::string action, std::string key) {
  PressStatusBind statusBind = wayToBindActions[action];
  std::string command;
  if (statusBind == press || statusBind == both) {
    command = "bind \"" + key + "\" down \"" + action + "\"";
    CMDMGR.run(command);
  };
  if (statusBind == release || statusBind == both) {
    command = "bind \"" + key + "\" up \"" + action + "\"";
    CMDMGR.run(command);
  };
}

void ActionBinding::unbind(std::string action, std::string key) {
  PressStatusBind statusBind = wayToBindActions[action];
  std::string command;
  if (statusBind == press || statusBind == both) {
    command = "unbind \"" + key + "\" down";
    CMDMGR.run(command);
  };
  if (statusBind == release || statusBind == both) {
    command = "unbind \"" + key + "\" up";
    CMDMGR.run(command);
  };
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>#pragma once

#include <type_traits>
#include <functional>
#include <memory>
#include <uv.h>
#include "loop.hpp"

namespace uvw {

template<typename>
struct HandleType { };

template<typename>
class Resource;

template<typename>
struct UVCallback;

template<typename... Args>
struct UVCallback<Args...> {
    template<typename T, T*(*F)(Args...)>
    static auto get(T *res);

private:
    template<typename T, T*(*F)(Args...)>
    static void proto(Args... args);
};

template<typename T>
class Resource: public std::enable_shared_from_this<T> {
    template<typename>
    friend struct UVCallback;

    static Resource<T>* closeCallback(uv_handle_t* h) {
        auto ptr = static_cast<Resource<T>*>(h->data);
        ptr->closeCb(UVWError{});
        return ptr;
    }

protected:
    template<typename U>
    explicit Resource(HandleType<U>, std::shared_ptr<Loop> r)
        : pLoop{std::move(r)}, handle{std::make_shared<U>()}
    { }

    template<typename U>
    U* get() const noexcept { return reinterpret_cast<U*>(handle.get()); }

    uv_loop_t* parent() const noexcept { return pLoop->loop.get(); }
    void reset() noexcept { ref = nullptr; }

public:
    explicit Resource(const Resource &) = delete;
    explicit Resource(Resource &&) = delete;

    ~Resource() { static_assert(std::is_base_of<Resource<T>, T>::value, "!"); }

    void operator=(const Resource &) = delete;
    void operator=(Resource &&) = delete;

    std::shared_ptr<Loop> loop() const noexcept { return pLoop; }

    bool active() const noexcept { return !(uv_is_active(get<uv_handle_t>()) == 0); }
    bool closing() const noexcept { return !(uv_is_closing(get<uv_handle_t>()) == 0); }

    void reference() noexcept { uv_ref(get<uv_handle_t>()); }
    void unreference() noexcept { uv_ref(get<uv_handle_t>()); }
    bool referenced() const noexcept { return !(uv_has_ref(get<uv_handle_t>()) == 0); }

    void close(std::function<void(UVWError)> cb) noexcept {
        using UVCB = UVCallback<uv_handle_t*>;
        auto func = UVCB::get<Resource<T>, &closeCallback>(this);
        closeCb = std::move(cb);
        uv_close(get<uv_handle_t>(), func);
    }

private:
    std::function<void(UVWError)> closeCb;
    std::shared_ptr<Loop> pLoop;
    std::shared_ptr<void> handle;
    std::shared_ptr<void> ref;
};

template<typename... Args>
template<typename T, T*(*F)(Args...)>
void UVCallback<Args...>::proto(Args... args) {
    T *res = F(std::forward<Args>(args)...);
    res->ref = nullptr;
}

template<typename... Args>
template<typename T, T*(*F)(Args...)>
auto UVCallback<Args...>::get(T *res) {
    res->template get<uv_handle_t>()->data = res;
    res->ref = res->shared_from_this();
    return &proto<T, F>;
}

}
<commit_msg>clean up<commit_after>#pragma once

#include <type_traits>
#include <functional>
#include <memory>
#include <uv.h>
#include "loop.hpp"

namespace uvw {

template<typename>
struct HandleType { };

template<typename>
class Resource;

template<typename... Args>
struct UVCallback {
    template<typename T, T*(*F)(Args...)>
    static auto get(T *res);

private:
    template<typename T, T*(*F)(Args...)>
    static void proto(Args... args);
};

template<typename T>
class Resource: public std::enable_shared_from_this<T> {
    template<typename...>
    friend struct UVCallback;

    static Resource<T>* closeCallback(uv_handle_t* h) {
        auto ptr = static_cast<Resource<T>*>(h->data);
        ptr->closeCb(UVWError{});
        return ptr;
    }

protected:
    template<typename U>
    explicit Resource(HandleType<U>, std::shared_ptr<Loop> r)
        : pLoop{std::move(r)}, handle{std::make_shared<U>()}
    { }

    template<typename U>
    U* get() const noexcept { return reinterpret_cast<U*>(handle.get()); }

    uv_loop_t* parent() const noexcept { return pLoop->loop.get(); }
    void reset() noexcept { ref = nullptr; }

public:
    explicit Resource(const Resource &) = delete;
    explicit Resource(Resource &&) = delete;

    ~Resource() { static_assert(std::is_base_of<Resource<T>, T>::value, "!"); }

    void operator=(const Resource &) = delete;
    void operator=(Resource &&) = delete;

    std::shared_ptr<Loop> loop() const noexcept { return pLoop; }

    bool active() const noexcept { return !(uv_is_active(get<uv_handle_t>()) == 0); }
    bool closing() const noexcept { return !(uv_is_closing(get<uv_handle_t>()) == 0); }

    void reference() noexcept { uv_ref(get<uv_handle_t>()); }
    void unreference() noexcept { uv_ref(get<uv_handle_t>()); }
    bool referenced() const noexcept { return !(uv_has_ref(get<uv_handle_t>()) == 0); }

    void close(std::function<void(UVWError)> cb) noexcept {
        using UVCB = UVCallback<uv_handle_t*>;
        auto func = UVCB::get<Resource<T>, &closeCallback>(this);
        closeCb = std::move(cb);
        uv_close(get<uv_handle_t>(), func);
    }

private:
    std::function<void(UVWError)> closeCb;
    std::shared_ptr<Loop> pLoop;
    std::shared_ptr<void> handle;
    std::shared_ptr<void> ref;
};

template<typename... Args>
template<typename T, T*(*F)(Args...)>
void UVCallback<Args...>::proto(Args... args) {
    T *res = F(std::forward<Args>(args)...);
    res->ref = nullptr;
}

template<typename... Args>
template<typename T, T*(*F)(Args...)>
auto UVCallback<Args...>::get(T *res) {
    res->template get<uv_handle_t>()->data = res;
    res->ref = res->shared_from_this();
    return &proto<T, F>;
}

}
<|endoftext|>


<commit_before>/*
** buffer.cpp
** Login : <hcuche@hcuche-de>
**
** Author(s):
**  - hcuche <hcuche@aldebaran-robotics.com>
*/

#include <qimessaging/buffer.hpp>
#include <qi/log.hpp>

#include <cstdio>
#include <cstring>
#include <ctype.h>

#include "src/buffer_p.hpp"

#include <iostream>

namespace qi
{

  BufferPrivate::BufferPrivate()
    : size(0)
    , cursor(0)
  {
  }

  Buffer::Buffer()
    : _p(new BufferPrivate) // TODO: add allocation-on-write.
  {
  }

  int Buffer::write(const void *data, size_t size)
  {
    if (sizeof(_p->data) - _p->size < size)
    {
      qiLogVerbose("qi.Buffer") << "write(" << size << ") failed, buffer size is " << _p->size;
      return -1;
    }

    memcpy(_p->data + _p->size, data, size);
    _p->size += size;

    return size;
  }

  int Buffer::read(void *data, size_t size)
  {
    if (_p->size - _p->cursor < size)
    {
      size = _p->size - _p->cursor;
    }

    memcpy(data, _p->data + _p->cursor, size);
    _p->cursor += size;

    return size;
  }

  void *Buffer::read(size_t size)
  {
    void *p = 0;
    if ((p = peek(size)))
    {
      seek(size);
    }

    return p;
  }

  size_t Buffer::size() const
  {
    return _p->size;
  }

  void *Buffer::reserve(size_t size)
  {
    void *p = _p->data + _p->cursor;
    _p->size = size;

    return p;
  }

  size_t Buffer::seek(long offset)
  {
    if (_p->cursor + offset <= _p->size)
    {
      _p->cursor += offset;
      return _p->cursor;
    }
    else
    {
      return -1;
    }
  }

  void *Buffer::peek(size_t QI_UNUSED(size)) const
  {
    return _p->cursor + _p->data;
  }

  void *Buffer::data() const
  {
    return _p->data;
  }

  void Buffer::dump() const
  {
    unsigned int i = 0;

    while (i < _p->size)
    {
      printf("%02x ", _p->data[i]);
      i++;
      if (i % 8 == 0) printf(" ");
      if (i % 16 == 0)
      {
        for (unsigned int j = i - 16; j < i ; j++)
        {
          printf("%c", isgraph(_p->data[j]) ? _p->data[j] : '.');
        }
        printf("\n");
      }
    }

    while (i % 16 != 0)
    {
      printf("   ");
      if (i % 8 == 0) printf(" ");
      i++;
    }
    printf(" ");
    for (unsigned int j = i - 16; j < _p->size; j++)
    {
      printf("%c", isgraph(_p->data[j]) ? _p->data[j] : '.');
    }
    printf("\n");
  }

} // !qi
<commit_msg>Fix read buffer. Return 0 if size needed is too big.<commit_after>/*
** buffer.cpp
** Login : <hcuche@hcuche-de>
**
** Author(s):
**  - hcuche <hcuche@aldebaran-robotics.com>
*/

#include <qimessaging/buffer.hpp>
#include <qi/log.hpp>

#include <cstdio>
#include <cstring>
#include <ctype.h>

#include "src/buffer_p.hpp"

#include <iostream>

namespace qi
{

  BufferPrivate::BufferPrivate()
    : size(0)
    , cursor(0)
  {
  }

  Buffer::Buffer()
    : _p(new BufferPrivate) // TODO: add allocation-on-write.
  {
  }

  int Buffer::write(const void *data, size_t size)
  {
    if (sizeof(_p->data) - _p->size < size)
    {
      qiLogVerbose("qi.Buffer") << "write(" << size << ") failed, buffer size is " << _p->size;
      return -1;
    }

    memcpy(_p->data + _p->size, data, size);
    _p->size += size;

    return size;
  }

  int Buffer::read(void *data, size_t size)
  {
    if (_p->size - _p->cursor < size)
    {
      size = _p->size - _p->cursor;
    }

    memcpy(data, _p->data + _p->cursor, size);
    _p->cursor += size;

    return size;
  }

  void *Buffer::read(size_t size)
  {
    void *p = 0;
    if ((p = peek(size)))
      seek(size);
    else
      return 0;

    return p;
  }

  size_t Buffer::size() const
  {
    return _p->size;
  }

  void *Buffer::reserve(size_t size)
  {
    void *p = _p->data + _p->cursor;
    _p->size = size;

    return p;
  }

  size_t Buffer::seek(long offset)
  {
    if (_p->cursor + offset <= _p->size)
    {
      _p->cursor += offset;
      return _p->cursor;
    }
    else
    {
      return -1;
    }
  }

  void *Buffer::peek(size_t size) const
  {
    if (_p->cursor + size <= _p->size)
      return _p->cursor + _p->data;
    else
      return 0;
  }

  void *Buffer::data() const
  {
    return _p->data;
  }

  void Buffer::dump() const
  {
    unsigned int i = 0;

    while (i < _p->size)
    {
      printf("%02x ", _p->data[i]);
      i++;
      if (i % 8 == 0) printf(" ");
      if (i % 16 == 0)
      {
        for (unsigned int j = i - 16; j < i ; j++)
        {
          printf("%c", isgraph(_p->data[j]) ? _p->data[j] : '.');
        }
        printf("\n");
      }
    }

    while (i % 16 != 0)
    {
      printf("   ");
      if (i % 8 == 0) printf(" ");
      i++;
    }
    printf(" ");
    for (unsigned int j = i - 16; j < _p->size; j++)
    {
      printf("%c", isgraph(_p->data[j]) ? _p->data[j] : '.');
    }
    printf("\n");
  }

} // !qi
<|endoftext|>


<commit_before>#include "DeviceMathTest.h"
#include <cuda/DeviceMath.h>
#include <cuda/DeviceCopy.h>
#include <common/EigenBridge.h>
#include "utils.h"

namespace sq = sqaod;

DeviceMathTest::DeviceMathTest(void) : MinimalTestSuite("DeviceMathTest") {
}

DeviceMathTest::~DeviceMathTest(void) {
}

void DeviceMathTest::setUp() {
    device_.initialize(0);
}

void DeviceMathTest::tearDown() {
    device_.finalize();
}

void DeviceMathTest::run(std::ostream &ostm) {

    sq::Dim dims[] = { {100, 100} , {32, 33}, {33, 32}};
    // sq::Dim dim(8, 5), sq::Dim dim(5, 8) sq::Dim dim(5, 5);
    for (int idx = 0; idx < 3; ++idx) {
        tests<double>(dims[idx]);
        tests<float>(dims[idx]);
    }
}

template<class real>
void DeviceMathTest::tests(const sqaod::Dim &dim) {

    DeviceMathType<real> devMath(device_);
    DeviceCopy devCopy(device_);

    typedef sq::MatrixType<real> HostMatrix;
    typedef sq::VectorType<real> HostVector;
    typedef sq::EigenMatrixType<real> EigenMatrix;
    typedef sq::EigenRowVectorType<real> EigenRowVector;
    typedef sq::EigenColumnVectorType<real> EigenColumnVector;
    typedef DeviceMatrixType<real> DeviceMatrix;
    typedef DeviceVectorType<real> DeviceVector;
    typedef DeviceScalarType<real> DeviceScalar;

    auto *alloc = device_.objectAllocator();

    DeviceMatrix dA, dB, dC, dD;
    DeviceVector dx, dy, dz;
    DeviceScalar da, db, dc;

    testcase("test zeros/eye") {
        sq::Dim dim1(dim.rows, dim.rows);
        alloc->allocate(&dA, dim1);
        devCopy(&dA, (real)0.); /* create zero matrix */
        device_.synchronize();
        TEST_ASSERT(dA == HostMatrix::zeros(dim1));

        devMath.setToDiagonals(&dA, real(1.));
        device_.synchronize();
        TEST_ASSERT(dA == HostMatrix::eye(dim.rows));
    }

    testcase("mat scale/sum") {
        HostMatrix hMat = testMatBalanced<real>(dim);
        devCopy(&dA, hMat);
        devMath.scale(&dB, 10., dA);
        device_.synchronize();
        hMat *= (real)10.;
        TEST_ASSERT(dB == hMat);

        devMath.sum(&da, real(3.), dB);
        device_.synchronize();
        TEST_ASSERT(da == real(3. * hMat.sum()));
        /* mulAddAssign */
        devMath.sum(&da, real(3.), dB, 1.);
        device_.synchronize();
        TEST_ASSERT(da == real(6. * hMat.sum()));
    }

    testcase("vec scale/sum") {
        HostVector hVec = testVec<real>(dim.cols);
        devCopy(&dx, hVec);

        devMath.scale(&dy, 10., dx);
        device_.synchronize();
        hVec *= (real)10.;
        TEST_ASSERT(dy == hVec);

        devMath.scale(&dy, 10., dx, 1.);
        device_.synchronize();
        hVec *= (real)2.;
        TEST_ASSERT(dy == hVec);

        devMath.sum(&da, real(3.), dy);
        device_.synchronize();
        TEST_ASSERT(da == real(3. * hVec.sum()));
        /* mulAddAssign */
        devMath.sum(&da, real(3.), dy, 2.);
        TEST_ASSERT(da == real((3. + 6.) * hVec.sum()));
    }

    testcase("scalar scale") {
        real hsc = 35.;
        devCopy(&da, hsc);

        devMath.scale(&db, 10., da);
        device_.synchronize();
        TEST_ASSERT(db == real(hsc * 10.));

        devMath.scale(&db, 10., da, 2.);
        device_.synchronize();
        TEST_ASSERT(db == real(hsc * 30.));
    }

    testcase("vector scale broadcast") {
        alloc->allocate(&dx, dim.rows);
        alloc->allocate(&dy, dim.cols);
        alloc->allocate(&da);

        /* initialize */
        devCopy(&da, (real)0.);
        devMath.scaleBroadcast(&dx, 1., da);
        HostVector x = HostVector::zeros(dim.rows);
        device_.synchronize();
        TEST_ASSERT(dx == x);

        devCopy(&da, (real)1.);
        devMath.scaleBroadcast(&dx, 2., da);
        x = HostVector::ones(dim.rows);
        x *= (real)2.;
        TEST_ASSERT(dx == x);

        devMath.scaleBroadcast(&dx, 2., da, 1.);
        x *= (real)2.;
        TEST_ASSERT(dx == x);
    }

    testcase("matrix scale broadcast") {
        alloc->allocate(&dA, dim);
        alloc->allocate(&dx, dim.cols);
        alloc->allocate(&dy, dim.rows);

        HostMatrix hmat(dim);
        HostVector x = testVec<real>(dim.cols);
        HostVector y = testVec<real>(dim.rows);
        devCopy(&dx, x);
        devCopy(&dy, y);

        devMath.scaleBroadcast(&dA, 1., dx, opRowwise);
        sq::mapTo(hmat).rowwise() = sq::mapToRowVector(x);
        device_.synchronize();
        TEST_ASSERT(dA == hmat);

        devMath.scaleBroadcast(&dA, 2., dx, opRowwise, 2.);
        sq::mapTo(hmat).rowwise() = 4. * sq::mapToRowVector(x);
        device_.synchronize();
        TEST_ASSERT(dA == hmat);

        devMath.scaleBroadcast(&dA, 1., dy, opColwise);
        sq::mapTo(hmat).colwise() = sq::mapToColumnVector(y);
        device_.synchronize();
        TEST_ASSERT(dA == hmat);

        devMath.scaleBroadcast(&dA, 2., dy, opColwise, 2.);
        sq::mapTo(hmat).colwise() = 4. * sq::mapToColumnVector(y);
        device_.synchronize();
        TEST_ASSERT(dA == hmat);
    }

    testcase("matrix sum") {
        HostMatrix hmat = testMat<real>(dim);
        alloc->allocate(&dA, dim);
        alloc->allocate(&da);

        devCopy(&dA, hmat);
        devMath.sum(&da, 1., dA);
        device_.synchronize();
        TEST_ASSERT(da == hmat.sum());

        devMath.sum(&da, 2., dA, 2.);
        device_.synchronize();
        TEST_ASSERT(da == real(4. * hmat.sum()));
    }

    testcase("vector sum") {
        HostVector hvec = testVec<real>(dim.rows);
        alloc->allocate(&dx, dim.rows);
        alloc->allocate(&da);

        devCopy(&dx, hvec);
        devMath.sum(&da, 1., dx);
        device_.synchronize();
        TEST_ASSERT(da == hvec.sum());

        devMath.sum(&da, 2., dx, 2.);
        device_.synchronize();
        TEST_ASSERT(da == real(4. * hvec.sum()));
    }

    testcase("diagonal sum") {
        HostMatrix hmat = testMat<real>(dim);
        alloc->allocate(&dA, dim);
        alloc->allocate(&da);

        devCopy(&dA, hmat);
        devMath.sumDiagonals(&da, dA);
        device_.synchronize();
        TEST_ASSERT(da == sq::mapTo(hmat).diagonal().sum());
    }
    testcase("sum batched") {
        alloc->allocate(&dA, dim);
        alloc->allocate(&dx, dim.rows); /* col vector */
        alloc->allocate(&dy, dim.cols); /* row vector */

        HostMatrix hmat = testMat<real>(dim);
        HostVector x = testVec<real>(dim.rows); /* col vector */
        HostVector y = testVec<real>(dim.cols); /* row vector */
        devCopy(&dA, hmat);
        devCopy(&dx, x);
        devCopy(&dy, y);

        devMath.sumBatched(&dx, 2., dA, opRowwise);

        HostVector hvec(dim.rows);
        mapToRowVector(hvec) = 2. * sq::mapTo(hmat).rowwise().sum();
        device_.synchronize();
        TEST_ASSERT(dx == hvec);

        devMath.sumBatched(&dy, 2., dA, opColwise);
        hvec.resize(dim.cols);
        mapToColumnVector(hvec) = 2. * sq::mapTo(hmat).colwise().sum();
        device_.synchronize();
        TEST_ASSERT(dy == hvec);
    }

    testcase("dot") {
        HostVector x = testVec<real>(dim.rows);
        HostVector y = testVec<real>(dim.rows);
        alloc->allocate(&dx, dim.rows);
        alloc->allocate(&dy, dim.rows);
        alloc->allocate(&da);

        devCopy(&dx, x);
        devCopy(&dy, y);
        devMath.dot(&da, 2., dx, dy);
        device_.synchronize();
        real product = mapToColumnVector(x).dot(mapToColumnVector(y));
        TEST_ASSERT(da == real(2. * product));

        devMath.dot(&da, 2., dx, dy, 1.);
        device_.synchronize();
        TEST_ASSERT(da == real(product * 4.));
    }

    testcase("dot batched") {
        alloc->allocate(&dA, dim);
        alloc->allocate(&dB, dim);
        alloc->allocate(&dx, dim.rows); /* col vector */
        alloc->allocate(&dy, dim.cols); /* row vector */

        HostMatrix A = testMatBalanced<real>(dim);
        HostMatrix B = testMatBalanced<real>(dim);
        devCopy(&dA, A);
        devCopy(&dB, B);
        devMath.dotBatched(&dx, 2., dA, opNone, dB, opNone);

        EigenMatrix eAB = sq::mapTo(A).array() * sq::mapTo(B).array();
        EigenRowVector vec = 2. * eAB.rowwise().sum();
        device_.synchronize();

        TEST_ASSERT(dx == sq::mapFrom(vec));

        devMath.dotBatched(&dy, 2., dA, opTranspose, dB, opTranspose);
        vec = 2. * eAB.colwise().sum();
        device_.synchronize();

        TEST_ASSERT(dy == sq::mapFrom(vec));
    }

    testcase("transpose") {
        HostMatrix hMat = testMat<real>(dim);
        devCopy(&dA, hMat);
        devMath.transpose(&dB, dA);
        device_.synchronize();
        HostMatrix hTrans(hMat.dim().transpose());
        sq::mapTo(hTrans) = sq::mapTo(hMat).transpose();
        TEST_ASSERT(dB == hTrans);
    }

    testcase("mvProduct") {
        HostMatrix A = testMatBalanced<real>(dim);
        HostVector x = testVecBalanced<real>(dim.cols);
        devCopy(&dA, A);
        devCopy(&dx, x);
        devMath.mvProduct(&dy, 0.5, dA, opNone, dx);
        EigenRowVector y = 0.5 * sq::mapTo(A) * sq::mapToColumnVector(x);
        device_.synchronize();
        TEST_ASSERT(dy == sq::mapFrom(y));

        HostMatrix B = testMatBalanced<real>(dim.transpose());
        devCopy(&dB, B);
        devMath.mvProduct(&dy, 0.25, dB, opTranspose, dx);
        y = sq::mapTo(B).transpose() * sq::mapToColumnVector(x);
        y *= 0.25;
        device_.synchronize();
        TEST_ASSERT(dy == sq::mapFrom(y));
    }

    testcase("mmProduct") {
        HostMatrix A = testMatBalanced<real>(dim);
        HostMatrix B = testMatBalanced<real>(dim.transpose());
        EigenMatrix C = 0.5 * sq::mapTo(A) * sq::mapTo(B);

        devCopy(&dA, A);
        devCopy(&dB, B);
        devMath.mmProduct(&dC, 0.5, dA, opNone, dB, opNone);
        device_.synchronize();
        TEST_ASSERT(dC == sq::mapFrom(C));

        HostMatrix At(dim.transpose());
        HostMatrix Bt(dim);
        sq::mapTo(At) = sq::mapTo(A).transpose();
        sq::mapTo(Bt) = sq::mapTo(B).transpose();
        alloc->deallocate(dA);
        alloc->deallocate(dB);
        alloc->deallocate(dC);
        devCopy(&dA, At);
        devCopy(&dB, Bt);
        devMath.mmProduct(&dC, 0.5, dA, opTranspose, dB, opTranspose);
        device_.synchronize();
        TEST_ASSERT(dC == sq::mapFrom(C));
    }

    testcase("vmvProduct") {
        HostMatrix A = testMatBalanced<real>(dim);
        HostVector x = testVecBalanced<real>(dim.cols);
        HostVector y = testVecBalanced<real>(dim.rows);
        real product = mapToRowVector(y) * sq::mapTo(A) * mapToColumnVector(x);

        devCopy(&dA, A);
        devCopy(&dx, x);
        devCopy(&dy, y);
        devMath.vmvProduct(&da, 2., dy, dA, dx);
        device_.synchronize();
        TEST_ASSERT(da == real(2. * product));
    }

    testcase("vmvProductBatched") {
        sqaod::Dim dimSq(dim.cols, dim.cols);
        HostMatrix A = testMatBalanced<real>(dimSq);
        HostMatrix X = testMatBalanced<real>(dim);
        HostMatrix Y = testMatBalanced<real>(dim);

        EigenMatrix AX = sq::mapTo(A) * sq::mapTo(X).transpose();
        EigenMatrix AXY = sq::mapTo(Y).array() * AX.transpose().array();
        EigenColumnVector z = 1. * AXY.rowwise().sum();

        devCopy(&dA, A);
        devCopy(&dB, X);
        devCopy(&dC, Y);
        devMath.vmvProductBatched(&dx, 2., dC, dA, dB);
        z *= 2.;
        device_.synchronize();
        TEST_ASSERT(dx == sq::mapFrom(z));
    }

    testcase("min matrix") {
        HostMatrix A = testMatBalanced<real>(dim);
        real vMin = sq::mapTo(A).minCoeff();
        devCopy(&dA, A);
        devMath.min(&da, dA);
        device_.synchronize();
        TEST_ASSERT(da == vMin);
    }

    testcase("min") {
        HostVector x = testVecBalanced<real>(dim.rows);
        real vMin = sq::mapToRowVector(x).minCoeff();
        devCopy(&dx, x);
        devMath.min(&da, dx);
        device_.synchronize();
        TEST_ASSERT(da == vMin);
    }

    alloc->deallocate(dA);
    alloc->deallocate(dB);
    alloc->deallocate(dC);
    alloc->deallocate(dD);
    alloc->deallocate(dx);
    alloc->deallocate(dy);
    alloc->deallocate(da);
}
<commit_msg>Using managed memory for ease of debug.<commit_after>#include "DeviceMathTest.h"
#include <cuda/DeviceMath.h>
#include <cuda/DeviceCopy.h>
#include <common/EigenBridge.h>
#include "utils.h"

namespace sq = sqaod;

DeviceMathTest::DeviceMathTest(void) : MinimalTestSuite("DeviceMathTest") {
}

DeviceMathTest::~DeviceMathTest(void) {
}

void DeviceMathTest::setUp() {
    device_.useManagedMemory(true);
    device_.initialize(0);
}

void DeviceMathTest::tearDown() {
    device_.finalize();
}

void DeviceMathTest::run(std::ostream &ostm) {

    sq::Dim dims[] = { {100, 100} , {32, 33}, {33, 32}};
    // sq::Dim dim(8, 5), sq::Dim dim(5, 8) sq::Dim dim(5, 5);
    for (int idx = 0; idx < 3; ++idx) {
        tests<double>(dims[idx]);
        tests<float>(dims[idx]);
    }
}

template<class real>
void DeviceMathTest::tests(const sqaod::Dim &dim) {

    DeviceMathType<real> devMath(device_);
    DeviceCopy devCopy(device_);

    typedef sq::MatrixType<real> HostMatrix;
    typedef sq::VectorType<real> HostVector;
    typedef sq::EigenMatrixType<real> EigenMatrix;
    typedef sq::EigenRowVectorType<real> EigenRowVector;
    typedef sq::EigenColumnVectorType<real> EigenColumnVector;
    typedef DeviceMatrixType<real> DeviceMatrix;
    typedef DeviceVectorType<real> DeviceVector;
    typedef DeviceScalarType<real> DeviceScalar;

    auto *alloc = device_.objectAllocator();

    DeviceMatrix dA, dB, dC, dD;
    DeviceVector dx, dy, dz;
    DeviceScalar da, db, dc;

    testcase("test zeros/eye") {
        sq::Dim dim1(dim.rows, dim.rows);
        alloc->allocate(&dA, dim1);
        devCopy(&dA, (real)0.); /* create zero matrix */
        device_.synchronize();
        TEST_ASSERT(dA == HostMatrix::zeros(dim1));

        devMath.setToDiagonals(&dA, real(1.));
        device_.synchronize();
        TEST_ASSERT(dA == HostMatrix::eye(dim.rows));
    }

    testcase("mat scale/sum") {
        HostMatrix hMat = testMatBalanced<real>(dim);
        devCopy(&dA, hMat);
        devMath.scale(&dB, 10., dA);
        device_.synchronize();
        hMat *= (real)10.;
        TEST_ASSERT(dB == hMat);

        devMath.sum(&da, real(3.), dB);
        device_.synchronize();
        TEST_ASSERT(da == real(3. * hMat.sum()));
        /* mulAddAssign */
        devMath.sum(&da, real(3.), dB, 1.);
        device_.synchronize();
        TEST_ASSERT(da == real(6. * hMat.sum()));
    }

    testcase("vec scale/sum") {
        HostVector hVec = testVec<real>(dim.cols);
        devCopy(&dx, hVec);

        devMath.scale(&dy, 10., dx);
        device_.synchronize();
        hVec *= (real)10.;
        TEST_ASSERT(dy == hVec);

        devMath.scale(&dy, 10., dx, 1.);
        device_.synchronize();
        hVec *= (real)2.;
        TEST_ASSERT(dy == hVec);

        devMath.sum(&da, real(3.), dy);
        device_.synchronize();
        TEST_ASSERT(da == real(3. * hVec.sum()));
        /* mulAddAssign */
        devMath.sum(&da, real(3.), dy, 2.);
        TEST_ASSERT(da == real((3. + 6.) * hVec.sum()));
    }

    testcase("scalar scale") {
        real hsc = 35.;
        devCopy(&da, hsc);

        devMath.scale(&db, 10., da);
        device_.synchronize();
        TEST_ASSERT(db == real(hsc * 10.));

        devMath.scale(&db, 10., da, 2.);
        device_.synchronize();
        TEST_ASSERT(db == real(hsc * 30.));
    }

    testcase("vector scale broadcast") {
        alloc->allocate(&dx, dim.rows);
        alloc->allocate(&dy, dim.cols);
        alloc->allocate(&da);

        /* initialize */
        devCopy(&da, (real)0.);
        devMath.scaleBroadcast(&dx, 1., da);
        HostVector x = HostVector::zeros(dim.rows);
        device_.synchronize();
        TEST_ASSERT(dx == x);

        devCopy(&da, (real)1.);
        devMath.scaleBroadcast(&dx, 2., da);
        x = HostVector::ones(dim.rows);
        x *= (real)2.;
        TEST_ASSERT(dx == x);

        devMath.scaleBroadcast(&dx, 2., da, 1.);
        x *= (real)2.;
        TEST_ASSERT(dx == x);
    }

    testcase("matrix scale broadcast") {
        alloc->allocate(&dA, dim);
        alloc->allocate(&dx, dim.cols);
        alloc->allocate(&dy, dim.rows);

        HostMatrix hmat(dim);
        HostVector x = testVec<real>(dim.cols);
        HostVector y = testVec<real>(dim.rows);
        devCopy(&dx, x);
        devCopy(&dy, y);

        devMath.scaleBroadcast(&dA, 1., dx, opRowwise);
        sq::mapTo(hmat).rowwise() = sq::mapToRowVector(x);
        device_.synchronize();
        TEST_ASSERT(dA == hmat);

        devMath.scaleBroadcast(&dA, 2., dx, opRowwise, 2.);
        sq::mapTo(hmat).rowwise() = 4. * sq::mapToRowVector(x);
        device_.synchronize();
        TEST_ASSERT(dA == hmat);

        devMath.scaleBroadcast(&dA, 1., dy, opColwise);
        sq::mapTo(hmat).colwise() = sq::mapToColumnVector(y);
        device_.synchronize();
        TEST_ASSERT(dA == hmat);

        devMath.scaleBroadcast(&dA, 2., dy, opColwise, 2.);
        sq::mapTo(hmat).colwise() = 4. * sq::mapToColumnVector(y);
        device_.synchronize();
        TEST_ASSERT(dA == hmat);
    }

    testcase("matrix sum") {
        HostMatrix hmat = testMat<real>(dim);
        alloc->allocate(&dA, dim);
        alloc->allocate(&da);

        devCopy(&dA, hmat);
        devMath.sum(&da, 1., dA);
        device_.synchronize();
        TEST_ASSERT(da == hmat.sum());

        devMath.sum(&da, 2., dA, 2.);
        device_.synchronize();
        TEST_ASSERT(da == real(4. * hmat.sum()));
    }

    testcase("vector sum") {
        HostVector hvec = testVec<real>(dim.rows);
        alloc->allocate(&dx, dim.rows);
        alloc->allocate(&da);

        devCopy(&dx, hvec);
        devMath.sum(&da, 1., dx);
        device_.synchronize();
        TEST_ASSERT(da == hvec.sum());

        devMath.sum(&da, 2., dx, 2.);
        device_.synchronize();
        TEST_ASSERT(da == real(4. * hvec.sum()));
    }

    testcase("diagonal sum") {
        HostMatrix hmat = testMat<real>(dim);
        alloc->allocate(&dA, dim);
        alloc->allocate(&da);

        devCopy(&dA, hmat);
        devMath.sumDiagonals(&da, dA);
        device_.synchronize();
        TEST_ASSERT(da == sq::mapTo(hmat).diagonal().sum());
    }
    testcase("sum batched") {
        alloc->allocate(&dA, dim);
        alloc->allocate(&dx, dim.rows); /* col vector */
        alloc->allocate(&dy, dim.cols); /* row vector */

        HostMatrix hmat = testMat<real>(dim);
        HostVector x = testVec<real>(dim.rows); /* col vector */
        HostVector y = testVec<real>(dim.cols); /* row vector */
        devCopy(&dA, hmat);
        devCopy(&dx, x);
        devCopy(&dy, y);

        devMath.sumBatched(&dx, 2., dA, opRowwise);

        HostVector hvec(dim.rows);
        mapToRowVector(hvec) = 2. * sq::mapTo(hmat).rowwise().sum();
        device_.synchronize();
        TEST_ASSERT(dx == hvec);

        devMath.sumBatched(&dy, 2., dA, opColwise);
        hvec.resize(dim.cols);
        mapToColumnVector(hvec) = 2. * sq::mapTo(hmat).colwise().sum();
        device_.synchronize();
        TEST_ASSERT(dy == hvec);
    }

    testcase("dot") {
        HostVector x = testVec<real>(dim.rows);
        HostVector y = testVec<real>(dim.rows);
        alloc->allocate(&dx, dim.rows);
        alloc->allocate(&dy, dim.rows);
        alloc->allocate(&da);

        devCopy(&dx, x);
        devCopy(&dy, y);
        devMath.dot(&da, 2., dx, dy);
        device_.synchronize();
        real product = mapToColumnVector(x).dot(mapToColumnVector(y));
        TEST_ASSERT(da == real(2. * product));

        devMath.dot(&da, 2., dx, dy, 1.);
        device_.synchronize();
        TEST_ASSERT(da == real(product * 4.));
    }

    testcase("dot batched") {
        alloc->allocate(&dA, dim);
        alloc->allocate(&dB, dim);
        alloc->allocate(&dx, dim.rows); /* col vector */
        alloc->allocate(&dy, dim.cols); /* row vector */

        HostMatrix A = testMatBalanced<real>(dim);
        HostMatrix B = testMatBalanced<real>(dim);
        devCopy(&dA, A);
        devCopy(&dB, B);
        devMath.dotBatched(&dx, 2., dA, opNone, dB, opNone);

        EigenMatrix eAB = sq::mapTo(A).array() * sq::mapTo(B).array();
        EigenRowVector vec = 2. * eAB.rowwise().sum();
        device_.synchronize();

        TEST_ASSERT(dx == sq::mapFrom(vec));

        devMath.dotBatched(&dy, 2., dA, opTranspose, dB, opTranspose);
        vec = 2. * eAB.colwise().sum();
        device_.synchronize();

        TEST_ASSERT(dy == sq::mapFrom(vec));
    }

    testcase("transpose") {
        HostMatrix hMat = testMat<real>(dim);
        devCopy(&dA, hMat);
        devMath.transpose(&dB, dA);
        device_.synchronize();
        HostMatrix hTrans(hMat.dim().transpose());
        sq::mapTo(hTrans) = sq::mapTo(hMat).transpose();
        TEST_ASSERT(dB == hTrans);
    }

    testcase("mvProduct") {
        HostMatrix A = testMatBalanced<real>(dim);
        HostVector x = testVecBalanced<real>(dim.cols);
        devCopy(&dA, A);
        devCopy(&dx, x);
        devMath.mvProduct(&dy, 0.5, dA, opNone, dx);
        EigenRowVector y = 0.5 * sq::mapTo(A) * sq::mapToColumnVector(x);
        device_.synchronize();
        TEST_ASSERT(dy == sq::mapFrom(y));

        HostMatrix B = testMatBalanced<real>(dim.transpose());
        devCopy(&dB, B);
        devMath.mvProduct(&dy, 0.25, dB, opTranspose, dx);
        y = sq::mapTo(B).transpose() * sq::mapToColumnVector(x);
        y *= 0.25;
        device_.synchronize();
        TEST_ASSERT(dy == sq::mapFrom(y));
    }

    testcase("mmProduct") {
        HostMatrix A = testMatBalanced<real>(dim);
        HostMatrix B = testMatBalanced<real>(dim.transpose());
        EigenMatrix C = 0.5 * sq::mapTo(A) * sq::mapTo(B);

        devCopy(&dA, A);
        devCopy(&dB, B);
        devMath.mmProduct(&dC, 0.5, dA, opNone, dB, opNone);
        device_.synchronize();
        TEST_ASSERT(dC == sq::mapFrom(C));

        HostMatrix At(dim.transpose());
        HostMatrix Bt(dim);
        sq::mapTo(At) = sq::mapTo(A).transpose();
        sq::mapTo(Bt) = sq::mapTo(B).transpose();
        alloc->deallocate(dA);
        alloc->deallocate(dB);
        alloc->deallocate(dC);
        devCopy(&dA, At);
        devCopy(&dB, Bt);
        devMath.mmProduct(&dC, 0.5, dA, opTranspose, dB, opTranspose);
        device_.synchronize();
        TEST_ASSERT(dC == sq::mapFrom(C));
    }

    testcase("vmvProduct") {
        HostMatrix A = testMatBalanced<real>(dim);
        HostVector x = testVecBalanced<real>(dim.cols);
        HostVector y = testVecBalanced<real>(dim.rows);
        real product = mapToRowVector(y) * sq::mapTo(A) * mapToColumnVector(x);

        devCopy(&dA, A);
        devCopy(&dx, x);
        devCopy(&dy, y);
        devMath.vmvProduct(&da, 2., dy, dA, dx);
        device_.synchronize();
        TEST_ASSERT(da == real(2. * product));
    }

    testcase("vmvProductBatched") {
        sqaod::Dim dimSq(dim.cols, dim.cols);
        HostMatrix A = testMatBalanced<real>(dimSq);
        HostMatrix X = testMatBalanced<real>(dim);
        HostMatrix Y = testMatBalanced<real>(dim);

        EigenMatrix AX = sq::mapTo(A) * sq::mapTo(X).transpose();
        EigenMatrix AXY = sq::mapTo(Y).array() * AX.transpose().array();
        EigenColumnVector z = 1. * AXY.rowwise().sum();

        devCopy(&dA, A);
        devCopy(&dB, X);
        devCopy(&dC, Y);
        devMath.vmvProductBatched(&dx, 2., dC, dA, dB);
        z *= 2.;
        device_.synchronize();
        TEST_ASSERT(dx == sq::mapFrom(z));
    }

    testcase("min matrix") {
        HostMatrix A = testMatBalanced<real>(dim);
        real vMin = sq::mapTo(A).minCoeff();
        devCopy(&dA, A);
        devMath.min(&da, dA);
        device_.synchronize();
        TEST_ASSERT(da == vMin);
    }

    testcase("min") {
        HostVector x = testVecBalanced<real>(dim.rows);
        real vMin = sq::mapToRowVector(x).minCoeff();
        devCopy(&dx, x);
        devMath.min(&da, dx);
        device_.synchronize();
        TEST_ASSERT(da == vMin);
    }

    alloc->deallocate(dA);
    alloc->deallocate(dB);
    alloc->deallocate(dC);
    alloc->deallocate(dD);
    alloc->deallocate(dx);
    alloc->deallocate(dy);
    alloc->deallocate(da);
}
<|endoftext|>