<commit_before>#include "gvki/Logger.h"
#include "gvki/PathSeperator.h"
#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <errno.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include "string.h"
#include "gvki/Debug.h"

#include <sys/stat.h>

#ifndef _WIN32

#include <dirent.h>
#include <unistd.h>
#define MKDIR_FAILS(d)     (mkdir(d, 0770) != 0)
#define DIR_ALREADY_EXISTS (errno == EEXIST)

static void checkDirectoryExists(const char* dirName) {
    DIR* dh = opendir(dirName);
    if (dh != NULL)
    {
        closedir(dh);
        return;
    }
    ERROR_MSG(strerror(errno) << ". Directory was :" << dirName);
    exit(1);
}

#else

#include <inttypes.h>
#include <Windows.h>
#define MKDIR_FAILS(d)     (CreateDirectory(ss.str().c_str(), NULL) == 0)
#define DIR_ALREADY_EXISTS (GetLastError() == ERROR_ALREADY_EXISTS)

static void checkDirectoryExists(const char* dirName) {
    DWORD ftyp = GetFileAttributesA(dirName);
    if ((ftyp != INVALID_FILE_ATTRIBUTES) && ftyp & FILE_ATTRIBUTE_DIRECTORY)
        return;
    ERROR_MSG(strerror(errno) << ". Directory was :" << dirName);
    exit(1);
}

#endif

using namespace std;
using namespace gvki;

// Maximum number of files or directories
// that can be created
static const int maxFiles = 10000;

Logger& Logger::Singleton()
{
    static Logger l;
    return l;
}

Logger::Logger()
{
    // FIXME: Reading from the environment probably doesn't belong in here
    // but it makes implementing the singleton a lot easier
    const char* doNotUseNumberedDirs = getenv("GVKI_NO_NUM_DIRS");
    if (doNotUseNumberedDirs)
    {
        DEBUG_MSG("Using manual directory");
        const char* rootDir = getenv("GVKI_ROOT");
        if (!rootDir)
        {
            ERROR_MSG("If using GVKI_NO_NUM_DIRS then GVKI_ROOT must be specified");
            _exit(1);
        }
        initDirectoryManual(rootDir);
    }
    else
    {
        DEBUG_MSG("Using numbered directory");
        initDirectoryNumbered();
    }
    checkDirectoryExists(this->directory.c_str());
}

void Logger::initDirectoryManual(const char* rootDir)
{
    assert(rootDir && "rootDir cannot be NULL");

    if (MKDIR_FAILS(rootDir))
    {
        ERROR_MSG("Failed to create directory \"" << rootDir << ": " << strerror(errno));
        _exit(1);
    }
    this->directory = std::string(rootDir);
}

void Logger::initDirectoryNumbered()
{
    int count = 0;
    bool success= false;

    std::string directoryPrefix;

    const char* envTemp = getenv("GVKI_ROOT");
    if (envTemp)
    {
        DEBUG_MSG("Using GVKI_ROOT value as destination for directories");

        checkDirectoryExists(envTemp);

        directoryPrefix = envTemp;

    }
    else
    {
        // Use the current working directory
        DEBUG_MSG("Using current working directory as destination for directories");

        // FIXME: Hard-coding this size is gross
        #define MAX_DIR_LENGTH 1024

#ifndef _WIN32
        char cwdArray[MAX_DIR_LENGTH];
        char* cwdResult = getcwd(cwdArray, sizeof(cwdArray)/sizeof(char));
        if (!cwdResult)
#else
        char cwdResult[MAX_DIR_LENGTH];
        if (GetCurrentDirectory(MAX_DIR_LENGTH, cwdResult) == 0)
#endif
        {
            ERROR_MSG(strerror(errno) << ". Could not read the current working directory");
            exit(1);
        }
        else
            directoryPrefix = cwdResult;
    }
    directoryPrefix += PATH_SEP "gvki";
    DEBUG_MSG("Directory prefix is \"" << directoryPrefix << "\"");

    // Keep trying a directoryPrefix name with a number as suffix
    // until we find an available one or we exhaust the maximum
    // allowed number
    while (count < maxFiles)
    {
        stringstream ss;
        ss <<  directoryPrefix << "-" << count;
        ++count;

        // Make the directoryPrefix
        if (MKDIR_FAILS(ss.str().c_str()))
        {
            if (!DIR_ALREADY_EXISTS)
            {
                ERROR_MSG(strerror(errno) << ". Directory was :" << directoryPrefix);
                exit(1);
            }

            // It already exists, try again
            continue;
        }

        this->directory = ss.str();
        success = true;
        break;
    }

    if (!success)
    {
        ERROR_MSG("Exhausted available directory names or couldn't create any");
        exit(1);
    }

    DEBUG_MSG("Directory used for logging is \"" << this->directory << "\"");

    openLog();
}

void Logger::openLog()
{
    // FIXME: We should use mkstemp() or something
    std::stringstream ss;
    ss << directory << PATH_SEP << "log.json";
    output = new std::ofstream(ss.str().c_str(), std::ofstream::out | std::ofstream::ate);

    if (! output->good())
    {
        ERROR_MSG("Failed to create file (" << ss.str() <<  ") to write log to");
        exit(1);
    }

    // Start of JSON array
    *output << "[" << std::endl;
}

void Logger::closeLog()
{
    // End of JSON array
    *output << std::endl << "]" << std::endl;
    output->close();
}

Logger::~Logger()
{
    closeLog();
    delete output;
}

void Logger::dump(cl_kernel k)
{
    // Output JSON format defined by
    // http://multicore.doc.ic.ac.uk/tools/GPUVerify/docs/json_format.html
    KernelInfo& ki = kernels[k];
    assert( programs.count(ki.program) == 1 && "cl_program missing");
    ProgramInfo& pi = programs[ki.program];

    static bool isFirst = true;

    if (!isFirst)
    {
        // Emit array element seperator
        // to seperate from previous dump
        *output << "," << endl;
    }
    isFirst = false;

    *output << "{" << endl << "\"language\": \"OpenCL\"," << endl;

    std::string kernelSourceFile = dumpKernelSource(ki);

    *output << "\"kernel_file\": \"" << kernelSourceFile << "\"," << endl;

    // FIXME: Teach GPUVerify how to handle non zero global_offset
    // FIXME: Document this json attribute!
    // Only emit global_offset if its non zero
    bool hasNonZeroGlobalOffset = false;
    for (int index=0; index < ki.globalWorkOffset.size() ; ++index)
    {
        if (ki.globalWorkOffset[index] != 0)
            hasNonZeroGlobalOffset = true;
    }
    if (hasNonZeroGlobalOffset)
    {
        *output << "\"global_offset\": ";
        printJSONArray(ki.globalWorkOffset);
        *output << "," << endl;
    }

    *output << "\"global_size\": ";
    printJSONArray(ki.globalWorkSize);
    *output << "," << endl;

    *output << "\"local_size\": ";
    printJSONArray(ki.localWorkSize);
    *output << "," << endl;

    *output << "\"compiler_flags\": \"" << pi.compileFlags << "\"," << endl;

    assert( (ki.globalWorkOffset.size() == ki.globalWorkSize.size()) &&
            (ki.globalWorkSize.size() == ki.localWorkSize.size()) &&
            "dimension mismatch");

    *output << "\"entry_point\": \"" << ki.entryPointName << "\"";

    // entry_point might be the last entry is there were no kernel args
    if (ki.arguments.size() == 0)
        *output << endl;
    else
    {
        *output << "," << endl << "\"kernel_arguments\": [" << endl;
        for (int argIndex=0; argIndex < ki.arguments.size() ; ++argIndex)
        {
            printJSONKernelArgumentInfo(ki.arguments[argIndex]);
            if (argIndex != (ki.arguments.size() -1))
                *output << "," << endl;
        }
        *output << endl << "]" << endl;
    }

    *output << "}";
}

void Logger::printJSONArray(std::vector<size_t>& array)
{
    *output << "[";
    for (int index=0; index < array.size(); ++index)
    {
        *output << array[index];

        if (index != (array.size() -1))
            *output << ", ";
    }
    *output << "]";
}

void Logger::printJSONKernelArgumentInfo(ArgInfo& ai)
{
    *output << "{";
    if (ai.argValue == NULL)
    {
        // NULL was passed to clSetKernelArg()
        // That implies its for unallocated memory
        *output << "\"type\": \"array\",";

        // If the arg is for local memory
        if (ai.argSize != sizeof(cl_mem) && ai.argSize != sizeof(cl_sampler))
        {
            // We assume this means this arguments is for local memory
            // where size actually means the sizeof the underlying buffer
            // rather than the size of the type.
            *output << "\"size\" : " << ai.argSize;
        }
        *output << "}";
        return;
    }

    // FIXME:
    // Eurgh... the spec says
    // ```
    // If the argument is a buffer object, the arg_value pointer can be NULL or
    // point to a NULL value in which case a NULL value will be used as the
    // value for the argument declared as a pointer to __global or __constant
    // memory in the kernel.  ```
    //
    // This makes it impossible (seeing as we don't know which address space
    // the arguments are in) to work out the argument type once derefencing the
    // pointer and finding it's equal zero because it could be a scalar constant
    // (of value 0) or it could be an unintialised array!

    // Hack:
    // It's hard to determine what type the argument is.
    // We can't dereference the void* to check if
    // it points to cl_mem we previously stored
    //
    // In some implementations cl_mem will be a pointer
    // which poses a risk if a scalar parameter of the same size
    // as the pointer type.
    if (ai.argSize == sizeof(cl_mem))
    {
       cl_mem mightBecl_mem = *((cl_mem*) ai.argValue);

       // We might be reading invalid data now
       if (buffers.count(mightBecl_mem) == 1)
       {
           // We're going to assume it's cl_mem that we saw before
           *output << "\"type\": \"array\",";
           BufferInfo& bi = buffers[mightBecl_mem];

           *output << "\"size\": " << bi.size << "}";
           return;
       }

    }

    // Hack: a similar hack of the image types
    if (ai.argSize == sizeof(cl_mem))
    {
       cl_mem mightBecl_mem = *((cl_mem*) ai.argValue);

       // We might be reading invalid data now
       if (images.count(mightBecl_mem) == 1)
       {
           // We're going to assume it's cl_mem that we saw before
           *output << "\"type\": \"image\"}";
           return;
       }

    }

    // Hack: a similar hack for the samplers
    if (ai.argSize == sizeof(cl_sampler))
    {
       cl_sampler mightBecl_sampler = *((cl_sampler*) ai.argValue);

       // We might be reading invalid data now
       if (samplers.count(mightBecl_sampler) == 1)
       {
           // We're going to assume it's cl_mem that we saw before
           *output << "\"type\": \"sampler\"}";
           return;
       }

    }

    // I guess it's scalar???
    *output << "\"type\": \"scalar\",";
    *output << " \"value\": \"0x";
    // Print the value as hex
    uint8_t* asByte = (uint8_t*) ai.argValue;
    // We assume the host is little endian so to print the values
    // we need to go through the array bytes backwards
    for (int byteIndex= ai.argSize -1; byteIndex >=0 ; --byteIndex)
    {
       *output << std::hex << std::setfill('0') << std::setw(2) << ( (unsigned) asByte[byteIndex]);
    }
    *output << "\"" << std::dec; //std::hex is sticky so switch back to decimal

    *output << "}";
    return;

}

static bool file_exists(std::string& name) {
	ifstream f(name.c_str());
	bool result = f.good();
	f.close();
	return result;
}

// Define the strict weak ordering over ProgramInfo instances
// Is this correct?
bool ProgramInfoCacheCompare::operator() (const ProgramInfo& lhs, const ProgramInfo& rhs) const
{
    // Use the fact that a strict-weak order is already defined over std::vector<>
    return lhs.sources < rhs.sources;
}

std::string Logger::dumpKernelSource(KernelInfo& ki)
{
    ProgramInfo& pi = programs[ki.program];

    // See if we can used a file that we already printed.
    // This avoid writing duplicate files.
    ProgCacheMapTy::iterator it = WrittenKernelFileCache.find(pi);
    if ( it != WrittenKernelFileCache.end() )
    {
        return it->second;
    }

    int count = 0;
    bool success = false;
    // FIXME: I really want a std::unique_ptr
    std::ofstream* kos = 0;
    std::string theKernelPath;
    while (count < maxFiles)
    {
        stringstream ss;
        ss << ki.entryPointName << "." << count << ".cl";

        ++count;

        std::string withDir = (directory + PATH_SEP) + ss.str();
        if (!file_exists(withDir))
        {
           // Use Binary mode to try avoid line ending issues on Windows
           kos = new std::ofstream(withDir.c_str(), std::ofstream::binary);

           if (!kos->good())
           {
                kos->close();
                delete kos;
                continue;
           }

           success = true;
           theKernelPath = ss.str();
           break;
        }
    }

    if (!success)
    {
        ERROR_MSG("Failed to log kernel output");
        return std::string("FIXME");
    }

    // Write kernel source

    for (vector<string>::const_iterator b = pi.sources.begin(), e = pi.sources.end(); b != e; ++b)
    {
        *kos << *b;
    }

    // Urgh this is bad, need RAII!
    kos->close();
    delete kos;

    // Store in cache
    assert(WrittenKernelFileCache.count(pi) == 0 && "ProgramInfo already in cache!");
    WrittenKernelFileCache[pi] = theKernelPath;

    return theKernelPath;
}
<commit_msg>Fix bug where openLog() was not called when using GVKI_NO_NUM_DIRS mode.<commit_after>#include "gvki/Logger.h"
#include "gvki/PathSeperator.h"
#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <errno.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include "string.h"
#include "gvki/Debug.h"

#include <sys/stat.h>

#ifndef _WIN32

#include <dirent.h>
#include <unistd.h>
#define MKDIR_FAILS(d)     (mkdir(d, 0770) != 0)
#define DIR_ALREADY_EXISTS (errno == EEXIST)

static void checkDirectoryExists(const char* dirName) {
    DIR* dh = opendir(dirName);
    if (dh != NULL)
    {
        closedir(dh);
        return;
    }
    ERROR_MSG(strerror(errno) << ". Directory was :" << dirName);
    exit(1);
}

#else

#include <inttypes.h>
#include <Windows.h>
#define MKDIR_FAILS(d)     (CreateDirectory(ss.str().c_str(), NULL) == 0)
#define DIR_ALREADY_EXISTS (GetLastError() == ERROR_ALREADY_EXISTS)

static void checkDirectoryExists(const char* dirName) {
    DWORD ftyp = GetFileAttributesA(dirName);
    if ((ftyp != INVALID_FILE_ATTRIBUTES) && ftyp & FILE_ATTRIBUTE_DIRECTORY)
        return;
    ERROR_MSG(strerror(errno) << ". Directory was :" << dirName);
    exit(1);
}

#endif

using namespace std;
using namespace gvki;

// Maximum number of files or directories
// that can be created
static const int maxFiles = 10000;

Logger& Logger::Singleton()
{
    static Logger l;
    return l;
}

Logger::Logger()
{
    // FIXME: Reading from the environment probably doesn't belong in here
    // but it makes implementing the singleton a lot easier
    const char* doNotUseNumberedDirs = getenv("GVKI_NO_NUM_DIRS");
    if (doNotUseNumberedDirs)
    {
        DEBUG_MSG("Using manual directory");
        const char* rootDir = getenv("GVKI_ROOT");
        if (!rootDir)
        {
            ERROR_MSG("If using GVKI_NO_NUM_DIRS then GVKI_ROOT must be specified");
            _exit(1);
        }
        initDirectoryManual(rootDir);
    }
    else
    {
        DEBUG_MSG("Using numbered directory");
        initDirectoryNumbered();
    }
    checkDirectoryExists(this->directory.c_str());
    DEBUG_MSG("Directory used for logging is \"" << this->directory << "\"");

    openLog();
}

void Logger::initDirectoryManual(const char* rootDir)
{
    assert(rootDir && "rootDir cannot be NULL");

    if (MKDIR_FAILS(rootDir))
    {
        ERROR_MSG("Failed to create directory \"" << rootDir << ": " << strerror(errno));
        _exit(1);
    }
    this->directory = std::string(rootDir);
}

void Logger::initDirectoryNumbered()
{
    int count = 0;
    bool success= false;

    std::string directoryPrefix;

    const char* envTemp = getenv("GVKI_ROOT");
    if (envTemp)
    {
        DEBUG_MSG("Using GVKI_ROOT value as destination for directories");

        checkDirectoryExists(envTemp);

        directoryPrefix = envTemp;

    }
    else
    {
        // Use the current working directory
        DEBUG_MSG("Using current working directory as destination for directories");

        // FIXME: Hard-coding this size is gross
        #define MAX_DIR_LENGTH 1024

#ifndef _WIN32
        char cwdArray[MAX_DIR_LENGTH];
        char* cwdResult = getcwd(cwdArray, sizeof(cwdArray)/sizeof(char));
        if (!cwdResult)
#else
        char cwdResult[MAX_DIR_LENGTH];
        if (GetCurrentDirectory(MAX_DIR_LENGTH, cwdResult) == 0)
#endif
        {
            ERROR_MSG(strerror(errno) << ". Could not read the current working directory");
            exit(1);
        }
        else
            directoryPrefix = cwdResult;
    }
    directoryPrefix += PATH_SEP "gvki";
    DEBUG_MSG("Directory prefix is \"" << directoryPrefix << "\"");

    // Keep trying a directoryPrefix name with a number as suffix
    // until we find an available one or we exhaust the maximum
    // allowed number
    while (count < maxFiles)
    {
        stringstream ss;
        ss <<  directoryPrefix << "-" << count;
        ++count;

        // Make the directoryPrefix
        if (MKDIR_FAILS(ss.str().c_str()))
        {
            if (!DIR_ALREADY_EXISTS)
            {
                ERROR_MSG(strerror(errno) << ". Directory was :" << directoryPrefix);
                exit(1);
            }

            // It already exists, try again
            continue;
        }

        this->directory = ss.str();
        success = true;
        break;
    }

    if (!success)
    {
        ERROR_MSG("Exhausted available directory names or couldn't create any");
        exit(1);
    }

}

void Logger::openLog()
{
    // FIXME: We should use mkstemp() or something
    std::stringstream ss;
    ss << directory << PATH_SEP << "log.json";
    output = new std::ofstream(ss.str().c_str(), std::ofstream::out | std::ofstream::ate);

    if (! output->good())
    {
        ERROR_MSG("Failed to create file (" << ss.str() <<  ") to write log to");
        exit(1);
    }

    // Start of JSON array
    *output << "[" << std::endl;
}

void Logger::closeLog()
{
    assert(output != NULL && "output must not be NULL");
    // End of JSON array
    *output << std::endl << "]" << std::endl;
    output->close();
}

Logger::~Logger()
{
    closeLog();
    delete output;
}

void Logger::dump(cl_kernel k)
{
    // Output JSON format defined by
    // http://multicore.doc.ic.ac.uk/tools/GPUVerify/docs/json_format.html
    KernelInfo& ki = kernels[k];
    assert( programs.count(ki.program) == 1 && "cl_program missing");
    ProgramInfo& pi = programs[ki.program];

    static bool isFirst = true;

    if (!isFirst)
    {
        // Emit array element seperator
        // to seperate from previous dump
        *output << "," << endl;
    }
    isFirst = false;

    *output << "{" << endl << "\"language\": \"OpenCL\"," << endl;

    std::string kernelSourceFile = dumpKernelSource(ki);

    *output << "\"kernel_file\": \"" << kernelSourceFile << "\"," << endl;

    // FIXME: Teach GPUVerify how to handle non zero global_offset
    // FIXME: Document this json attribute!
    // Only emit global_offset if its non zero
    bool hasNonZeroGlobalOffset = false;
    for (int index=0; index < ki.globalWorkOffset.size() ; ++index)
    {
        if (ki.globalWorkOffset[index] != 0)
            hasNonZeroGlobalOffset = true;
    }
    if (hasNonZeroGlobalOffset)
    {
        *output << "\"global_offset\": ";
        printJSONArray(ki.globalWorkOffset);
        *output << "," << endl;
    }

    *output << "\"global_size\": ";
    printJSONArray(ki.globalWorkSize);
    *output << "," << endl;

    *output << "\"local_size\": ";
    printJSONArray(ki.localWorkSize);
    *output << "," << endl;

    *output << "\"compiler_flags\": \"" << pi.compileFlags << "\"," << endl;

    assert( (ki.globalWorkOffset.size() == ki.globalWorkSize.size()) &&
            (ki.globalWorkSize.size() == ki.localWorkSize.size()) &&
            "dimension mismatch");

    *output << "\"entry_point\": \"" << ki.entryPointName << "\"";

    // entry_point might be the last entry is there were no kernel args
    if (ki.arguments.size() == 0)
        *output << endl;
    else
    {
        *output << "," << endl << "\"kernel_arguments\": [" << endl;
        for (int argIndex=0; argIndex < ki.arguments.size() ; ++argIndex)
        {
            printJSONKernelArgumentInfo(ki.arguments[argIndex]);
            if (argIndex != (ki.arguments.size() -1))
                *output << "," << endl;
        }
        *output << endl << "]" << endl;
    }

    *output << "}";
}

void Logger::printJSONArray(std::vector<size_t>& array)
{
    *output << "[";
    for (int index=0; index < array.size(); ++index)
    {
        *output << array[index];

        if (index != (array.size() -1))
            *output << ", ";
    }
    *output << "]";
}

void Logger::printJSONKernelArgumentInfo(ArgInfo& ai)
{
    *output << "{";
    if (ai.argValue == NULL)
    {
        // NULL was passed to clSetKernelArg()
        // That implies its for unallocated memory
        *output << "\"type\": \"array\",";

        // If the arg is for local memory
        if (ai.argSize != sizeof(cl_mem) && ai.argSize != sizeof(cl_sampler))
        {
            // We assume this means this arguments is for local memory
            // where size actually means the sizeof the underlying buffer
            // rather than the size of the type.
            *output << "\"size\" : " << ai.argSize;
        }
        *output << "}";
        return;
    }

    // FIXME:
    // Eurgh... the spec says
    // ```
    // If the argument is a buffer object, the arg_value pointer can be NULL or
    // point to a NULL value in which case a NULL value will be used as the
    // value for the argument declared as a pointer to __global or __constant
    // memory in the kernel.  ```
    //
    // This makes it impossible (seeing as we don't know which address space
    // the arguments are in) to work out the argument type once derefencing the
    // pointer and finding it's equal zero because it could be a scalar constant
    // (of value 0) or it could be an unintialised array!

    // Hack:
    // It's hard to determine what type the argument is.
    // We can't dereference the void* to check if
    // it points to cl_mem we previously stored
    //
    // In some implementations cl_mem will be a pointer
    // which poses a risk if a scalar parameter of the same size
    // as the pointer type.
    if (ai.argSize == sizeof(cl_mem))
    {
       cl_mem mightBecl_mem = *((cl_mem*) ai.argValue);

       // We might be reading invalid data now
       if (buffers.count(mightBecl_mem) == 1)
       {
           // We're going to assume it's cl_mem that we saw before
           *output << "\"type\": \"array\",";
           BufferInfo& bi = buffers[mightBecl_mem];

           *output << "\"size\": " << bi.size << "}";
           return;
       }

    }

    // Hack: a similar hack of the image types
    if (ai.argSize == sizeof(cl_mem))
    {
       cl_mem mightBecl_mem = *((cl_mem*) ai.argValue);

       // We might be reading invalid data now
       if (images.count(mightBecl_mem) == 1)
       {
           // We're going to assume it's cl_mem that we saw before
           *output << "\"type\": \"image\"}";
           return;
       }

    }

    // Hack: a similar hack for the samplers
    if (ai.argSize == sizeof(cl_sampler))
    {
       cl_sampler mightBecl_sampler = *((cl_sampler*) ai.argValue);

       // We might be reading invalid data now
       if (samplers.count(mightBecl_sampler) == 1)
       {
           // We're going to assume it's cl_mem that we saw before
           *output << "\"type\": \"sampler\"}";
           return;
       }

    }

    // I guess it's scalar???
    *output << "\"type\": \"scalar\",";
    *output << " \"value\": \"0x";
    // Print the value as hex
    uint8_t* asByte = (uint8_t*) ai.argValue;
    // We assume the host is little endian so to print the values
    // we need to go through the array bytes backwards
    for (int byteIndex= ai.argSize -1; byteIndex >=0 ; --byteIndex)
    {
       *output << std::hex << std::setfill('0') << std::setw(2) << ( (unsigned) asByte[byteIndex]);
    }
    *output << "\"" << std::dec; //std::hex is sticky so switch back to decimal

    *output << "}";
    return;

}

static bool file_exists(std::string& name) {
	ifstream f(name.c_str());
	bool result = f.good();
	f.close();
	return result;
}

// Define the strict weak ordering over ProgramInfo instances
// Is this correct?
bool ProgramInfoCacheCompare::operator() (const ProgramInfo& lhs, const ProgramInfo& rhs) const
{
    // Use the fact that a strict-weak order is already defined over std::vector<>
    return lhs.sources < rhs.sources;
}

std::string Logger::dumpKernelSource(KernelInfo& ki)
{
    ProgramInfo& pi = programs[ki.program];

    // See if we can used a file that we already printed.
    // This avoid writing duplicate files.
    ProgCacheMapTy::iterator it = WrittenKernelFileCache.find(pi);
    if ( it != WrittenKernelFileCache.end() )
    {
        return it->second;
    }

    int count = 0;
    bool success = false;
    // FIXME: I really want a std::unique_ptr
    std::ofstream* kos = 0;
    std::string theKernelPath;
    while (count < maxFiles)
    {
        stringstream ss;
        ss << ki.entryPointName << "." << count << ".cl";

        ++count;

        std::string withDir = (directory + PATH_SEP) + ss.str();
        if (!file_exists(withDir))
        {
           // Use Binary mode to try avoid line ending issues on Windows
           kos = new std::ofstream(withDir.c_str(), std::ofstream::binary);

           if (!kos->good())
           {
                kos->close();
                delete kos;
                continue;
           }

           success = true;
           theKernelPath = ss.str();
           break;
        }
    }

    if (!success)
    {
        ERROR_MSG("Failed to log kernel output");
        return std::string("FIXME");
    }

    // Write kernel source

    for (vector<string>::const_iterator b = pi.sources.begin(), e = pi.sources.end(); b != e; ++b)
    {
        *kos << *b;
    }

    // Urgh this is bad, need RAII!
    kos->close();
    delete kos;

    // Store in cache
    assert(WrittenKernelFileCache.count(pi) == 0 && "ProgramInfo already in cache!");
    WrittenKernelFileCache[pi] = theKernelPath;

    return theKernelPath;
}
<|endoftext|>


<commit_before>#include "trainingstab.h"
#include "ui_trainingstab.h"

#include <QMessageBox>
#include <QSqlField>

/**
 * @brief Creates a tab widget for management training.
 * @param parent Parent widget.
 */
TrainingsTab::TrainingsTab(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::TrainingsTab)
{
    ui->setupUi(this);

    connect(ui->btnAdd, SIGNAL(clicked()), this, SLOT(showAddTrainingDlg()));
    connect(ui->btnEdit, SIGNAL(clicked()), this, SLOT(showEditTrainingDlg()));
    connect(ui->btnDelete, SIGNAL(clicked()), this, SLOT(deleteTraining()));
    connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changeTeamFilter(int)));
    connect(ui->listView, SIGNAL(activated(QModelIndex)), this, SLOT(changeTrainingFilter(QModelIndex)));
    connect(ui->listView, SIGNAL(entered(QModelIndex)), this, SLOT(changeTrainingFilter(QModelIndex)));
    connect(ui->btnRecordAttendance, SIGNAL(clicked()), this, SLOT(showRecordAttendanceDlg()));

    teamModel = new TeamModel;
    trainingModel = new TrainingModel;
    trainingModel->setFilter("team_id=0");

    ui->listView->setModel(teamModel);
    ui->listView->setModelColumn(1);

    ui->treeView->setModel(trainingModel);
    ui->treeView->hideColumn(0);
    ui->treeView->hideColumn(4);
    ui->treeView->sortByColumn(1, Qt::AscendingOrder);
    ui->treeView->header()->resizeSection(1, 140);
    ui->treeView->header()->resizeSection(2, 250);

    categoryModel = new CategoryModel(this);
    ui->comboBox->insertItems(1, categoryModel->categoryList());
}

/**
 * @brief Show the dialog for adding training.
 */
void TrainingsTab::showAddTrainingDlg()
{
    addTrainingDlg = new AddTrainingDlg(trainingModel);
    addTrainingDlg->exec();
    delete addTrainingDlg;
}

/**
 * @brief Displays the dialog for editting selected training.
 */
void TrainingsTab::showEditTrainingDlg()
{
    QItemSelectionModel *selmodel = ui->treeView->selectionModel();
    QModelIndexList list = selmodel->selectedIndexes();

    if(list.size() == 0) return;

    addTrainingDlg = new AddTrainingDlg(trainingModel, list.at(0).row());
    addTrainingDlg->exec();
    delete addTrainingDlg;
}

/**
 * @brief Deletes the selected trainings.
 */
void TrainingsTab::deleteTraining()
{
    QItemSelectionModel *selmodel = ui->treeView->selectionModel();
    QModelIndexList list = selmodel->selectedIndexes();

    if(list.size() > 0)
        if(QMessageBox::question(this, tr("Delete trainings"), tr("Really delete trainings?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) return;

    for(int i = 0; i < list.size(); i++)
        trainingModel->removeRow(list.at(i).row());
    trainingModel->submitAll();
}

/**
 * @brief Change trainings filter by team ID.
 * @param index Index of selected team.
 */
void TrainingsTab::changeTrainingFilter(QModelIndex index)
{
    int idx = teamModel->record(index.row()).field("id").value().toInt();
    trainingModel->setFilter("team_id="+QString::number(idx));
}

/**
 * @brief Change team filter by category ID.
 * @param index Index of selected category.
 */
void TrainingsTab::changeTeamFilter(int index)
{
    if(index == 0)
        teamModel->setFilter(QString());
    else
    {
        int idx = categoryModel->record(index-1).field("id").value().toInt();
        teamModel->setFilter("team_category_id = "+QString::number(idx));
    }
    trainingModel->setFilter("team_id=0");
}

/**
 * @brief Displays the dialog for record attendance of selected training.
 */
void TrainingsTab::showRecordAttendanceDlg()
{
    QItemSelectionModel *selmodel = ui->treeView->selectionModel();
    QModelIndexList list = selmodel->selectedRows();

    if(list.size() == 0) return;

    recordAttendanceDlg = new RecordAttendanceDlg(trainingModel->record(list.at(0).row()));
    recordAttendanceDlg->exec();
    delete recordAttendanceDlg;
}

/**
 * @brief Destruct the widget.
 */
TrainingsTab::~TrainingsTab()
{
    delete ui;
}
<commit_msg>Delete attendance records, when deleting training.<commit_after>#include "trainingstab.h"
#include "ui_trainingstab.h"

#include <QMessageBox>
#include <QSqlField>

/**
 * @brief Creates a tab widget for management training.
 * @param parent Parent widget.
 */
TrainingsTab::TrainingsTab(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::TrainingsTab)
{
    ui->setupUi(this);

    connect(ui->btnAdd, SIGNAL(clicked()), this, SLOT(showAddTrainingDlg()));
    connect(ui->btnEdit, SIGNAL(clicked()), this, SLOT(showEditTrainingDlg()));
    connect(ui->btnDelete, SIGNAL(clicked()), this, SLOT(deleteTraining()));
    connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changeTeamFilter(int)));
    connect(ui->listView, SIGNAL(activated(QModelIndex)), this, SLOT(changeTrainingFilter(QModelIndex)));
    connect(ui->listView, SIGNAL(entered(QModelIndex)), this, SLOT(changeTrainingFilter(QModelIndex)));
    connect(ui->btnRecordAttendance, SIGNAL(clicked()), this, SLOT(showRecordAttendanceDlg()));

    teamModel = new TeamModel;
    trainingModel = new TrainingModel;
    trainingModel->setFilter("team_id=0");

    ui->listView->setModel(teamModel);
    ui->listView->setModelColumn(1);

    ui->treeView->setModel(trainingModel);
    ui->treeView->hideColumn(0);
    ui->treeView->hideColumn(4);
    ui->treeView->sortByColumn(1, Qt::AscendingOrder);
    ui->treeView->header()->resizeSection(1, 140);
    ui->treeView->header()->resizeSection(2, 250);

    categoryModel = new CategoryModel(this);
    ui->comboBox->insertItems(1, categoryModel->categoryList());
}

/**
 * @brief Show the dialog for adding training.
 */
void TrainingsTab::showAddTrainingDlg()
{
    addTrainingDlg = new AddTrainingDlg(trainingModel);
    addTrainingDlg->exec();
    delete addTrainingDlg;
}

/**
 * @brief Displays the dialog for editting selected training.
 */
void TrainingsTab::showEditTrainingDlg()
{
    QItemSelectionModel *selmodel = ui->treeView->selectionModel();
    QModelIndexList list = selmodel->selectedIndexes();

    if(list.size() == 0) return;

    addTrainingDlg = new AddTrainingDlg(trainingModel, list.at(0).row());
    addTrainingDlg->exec();
    delete addTrainingDlg;
}

/**
 * @brief Deletes the selected trainings.
 */
void TrainingsTab::deleteTraining()
{
    QItemSelectionModel *selmodel = ui->treeView->selectionModel();
    QModelIndexList list = selmodel->selectedIndexes();

    if(list.size() > 0)
        if(QMessageBox::question(this, tr("Delete trainings"), tr("Really delete trainings?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) return;

    QSqlTableModel *model = new QSqlTableModel();
    model->setTable("attendance");

    for(int i = 0; i < list.size(); i++)
    {
        int row = list.at(i).row();
        int id = trainingModel->record(row).field("id").value().toInt();
        trainingModel->removeRow(row);

        model->setFilter("training_id = "+QString::number(id));
        model->select();
        model->removeRows(0, model->rowCount());
        model->submitAll();
    }
    delete model;
    trainingModel->submitAll();
}

/**
 * @brief Change trainings filter by team ID.
 * @param index Index of selected team.
 */
void TrainingsTab::changeTrainingFilter(QModelIndex index)
{
    int idx = teamModel->record(index.row()).field("id").value().toInt();
    trainingModel->setFilter("team_id="+QString::number(idx));
}

/**
 * @brief Change team filter by category ID.
 * @param index Index of selected category.
 */
void TrainingsTab::changeTeamFilter(int index)
{
    if(index == 0)
        teamModel->setFilter(QString());
    else
    {
        int idx = categoryModel->record(index-1).field("id").value().toInt();
        teamModel->setFilter("team_category_id = "+QString::number(idx));
    }
    trainingModel->setFilter("team_id=0");
}

/**
 * @brief Displays the dialog for record attendance of selected training.
 */
void TrainingsTab::showRecordAttendanceDlg()
{
    QItemSelectionModel *selmodel = ui->treeView->selectionModel();
    QModelIndexList list = selmodel->selectedRows();

    if(list.size() == 0) return;

    recordAttendanceDlg = new RecordAttendanceDlg(trainingModel->record(list.at(0).row()));
    recordAttendanceDlg->exec();
    delete recordAttendanceDlg;
}

/**
 * @brief Destruct the widget.
 */
TrainingsTab::~TrainingsTab()
{
    delete ui;
}
<|endoftext|>


<commit_before>#include <tiramisu/tiramisu.h>
#include <tiramisu/block.h>

#include "configuration.h"

using namespace tiramisu;

int main(int argc, char **argv)
{
    // Double tiling with Register and Shared memory
    // Fused A_reg and non-square tiling

    tiramisu::init("matmul");

    // -------------------------------------------------------
    // Layer I
    // -------------------------------------------------------

    // Declare loop iterators
    var i("i", 0, M), j("j", 0, N), k("k", 0, K);
    var i0("i0", 0, M / R_BLOCK_I), i1("i1", 0, R_BLOCK_I), j0("j0", 0, N / R_BLOCK_J), j1("j1", 0, R_BLOCK_J), k0("k0", 0, K / BLOCK), k1("k1", 0, BLOCK);
    var k0_skiplast("k0", 0, K / BLOCK - 1);
    var i00("i00"), i01("i01"), j00("j00"), j01("j01");

    // Declare cpu buffers.
    buffer b_A("b_A", {M, K}, DATA_PTYPE, a_input);
    buffer b_B("b_B", {K, N}, DATA_PTYPE, a_input);
    buffer b_C("b_C", {M, N}, DATA_PTYPE, a_output);
    buffer b_Consts("b_Consts", {2}, DATA_PTYPE, a_input);
    // Declare gpu buffers.
    buffer b_A_glb("b_A_glb", {M, K}, DATA_PTYPE, a_temporary);
    buffer b_B_glb("b_B_glb", {K, N}, DATA_PTYPE, a_temporary);
    buffer b_C_glb("b_C_glb", {M, N}, DATA_PTYPE, a_temporary);
    // "+ 1" to reduce shared memory bank conflicts
    buffer b_A_shr("b_A_shr", {2, BLOCK, BLOCK * R_BLOCK_I + 1}, DATA_PTYPE, a_temporary);
    buffer b_B_shr("b_B_shr", {2, BLOCK, BLOCK * R_BLOCK_J}, DATA_PTYPE, a_temporary);
    buffer b_A_reg("b_A_reg", {1}, DATA_PTYPE, a_temporary);
    buffer b_B_reg("b_B_reg", {R_BLOCK_J}, DATA_PTYPE, a_temporary);
    buffer b_acc("b_acc", {R_BLOCK_I, R_BLOCK_J}, DATA_PTYPE, a_temporary);
    b_A_glb.tag_gpu_global();
    b_B_glb.tag_gpu_global();
    b_C_glb.tag_gpu_global();
    b_A_shr.tag_gpu_shared();
    b_B_shr.tag_gpu_shared();
    b_A_reg.tag_gpu_register();
    b_B_reg.tag_gpu_local();
    b_acc.tag_gpu_local();

    // Declare input wrappers
    input c_A_glb({i0, j0, k0, i1}, DATA_PTYPE);
    input c_A_shr({i0, j0, k0, k1, i1}, DATA_PTYPE);
    input c_A({i, k}, DATA_PTYPE);
    input c_B_glb({i0, j0, k0, j1}, DATA_PTYPE);
    input c_B_shr({i0, j0, k0, k1, j1}, DATA_PTYPE);
    input c_B({k, j}, DATA_PTYPE);
    input c_Consts({i}, DATA_PTYPE);
    constant c_alpha("alpha", c_Consts(0));
    constant c_beta("beta", c_Consts(1));
    // Declare computations
    computation c_A_glb_to_shr_pre({i0, j0, i1}, c_A_glb(i0, j0, 0, i1));
    computation c_A_glb_to_shr({i0, j0, k0_skiplast, i1}, c_A_glb(i0, j0, k0_skiplast + 1, i1));
    computation c_A_shr_to_reg({i0, j0, k0, k1, i1}, c_A_shr(i0, j0, k0, k1, i1));
    computation c_B_glb_to_shr_pre({i0, j0, j1}, c_B_glb(i0, j0, 0, j1));
    computation c_B_glb_to_shr({i0, j0, k0_skiplast, j1}, c_B_glb(i0, j0, k0_skiplast + 1, j1));
    computation c_B_shr_to_reg({i0, j0, k0, k1, j1}, c_B_shr(i0, j0, k0, k1, j1));
    computation c_acc_init({i, j}, (float) 0);
    computation c_acc({i, j, k}, DATA_PTYPE);
    c_acc.set_expression(c_acc(i, j, 0) + c_A(i, k) * c_B(k, j));
    computation c_C({i, j}, DATA_PTYPE);
    c_C.set_expression(c_acc(i, j, 0) * c_alpha + c_C(i, j) * c_beta);
    // Declare declarations
    computation c_A_shr_dec({i0, j0}, allocate(b_A_shr));
    computation c_A_reg_dec({i0, j0}, allocate(b_A_reg));
    computation c_B_shr_dec({i0, j0}, allocate(b_B_shr));
    computation c_B_reg_dec({i0, j0}, allocate(b_B_reg));
    computation c_acc_dec({i0, j0}, allocate(b_acc));
    // Declare synchronizer computations
    computation c_sync1({i0, j0}, tiramisu::sync());
    computation c_sync2({i0, j0, k0}, tiramisu::sync());
    // Declare host-gpu transfer computations.
    computation copy_A_to_device({}, memcpy(b_A, b_A_glb));
    computation copy_B_to_device({}, memcpy(b_B, b_B_glb));
    computation copy_C_to_device({}, memcpy(b_C, b_C_glb));
    computation copy_C_to_host({}, memcpy(b_C_glb, b_C));

    computation time_start({var("dummy", 0, 1)}, expr(o_call, "get_time", {int32_t(0)}, p_float32));
    computation time_end({var("dummy", 0, 1)}, expr(o_call, "get_time", {int32_t(0)}, p_float32));
    // Synchronize before timing
    computation final_sync({var("dummy", 0, 1)}, cuda_stream_synchronize());

    // -------------------------------------------------------
    // Layer II
    // -------------------------------------------------------

    // Scheduling commands

    c_acc_init.tile(i, j, R_BLOCK_I, R_BLOCK_J, i0, j0, i1, j1);
    c_acc.tile(i, j, R_BLOCK_I, R_BLOCK_J, i0, j0, i1, j1);
    c_acc.interchange(j1, k);
    c_acc.interchange(i1, k);
    c_acc.split(k, BLOCK, k0, k1);
    c_C.tile(i, j, R_BLOCK_I, R_BLOCK_J, i0, j0, i1, j1);

    block kernel_block({&c_acc_dec, &c_acc_init, &c_acc, &c_C,
            &c_A_shr_dec, &c_A_reg_dec, &c_B_shr_dec, &c_B_reg_dec,
            &c_A_glb_to_shr_pre, &c_A_glb_to_shr, &c_A_shr_to_reg,
            &c_B_glb_to_shr_pre, &c_B_glb_to_shr, &c_B_shr_to_reg,
            &c_sync1, &c_sync2});
    kernel_block.gpu_tile(i0, j0, BLOCK, BLOCK, i00, j00, i01, j01);

    copy_A_to_device.then(copy_B_to_device, computation::root)
                    .then(copy_C_to_device, computation::root)
                    .then(time_start, computation::root)
                    .then(c_A_shr_dec, computation::root)
                    .then(c_B_shr_dec, j01)
                    .then(c_A_reg_dec, j01)
                    .then(c_B_reg_dec, j01)
                    .then(c_acc_dec, j01)
                    .then(c_acc_init, j01)
                    .then(c_A_glb_to_shr_pre, j01)
                    .then(c_B_glb_to_shr_pre, j01)
                    .then(c_sync1, j01)
                    .then(c_A_glb_to_shr, j01)
                    .then(c_B_glb_to_shr, k0)
                    .then(c_B_shr_to_reg, k0)
                    .then(c_A_shr_to_reg, k1)
                    .then(c_acc, i1)
                    .then(c_sync2, k0)
                    .then(c_C, j01)
                    .then(final_sync, computation::root)
                    .then(time_end, computation::root)
                    .then(copy_C_to_host, computation::root);

    // -------------------------------------------------------
    // Layer III
    // -------------------------------------------------------

    c_A_glb.store_in(&b_A_glb, {i0 * R_BLOCK_I + i1, k0 * BLOCK + j0 % BLOCK});
    // Note the transpose:
    c_A_glb_to_shr_pre.store_in(&b_A_shr, {0, j0 % BLOCK, i0 % BLOCK * R_BLOCK_I + i1});
    c_A_glb_to_shr.store_in(&b_A_shr, {(k0_skiplast + 1) % 2, j0 % BLOCK, i0 % BLOCK * R_BLOCK_I + i1});
    c_A_shr.store_in(&b_A_shr, {k0 % 2, k1, i0 % BLOCK * R_BLOCK_I + i1});
    c_A_shr_to_reg.store_in(&b_A_reg, {0});
    // Note that we use a transposed mapping to assure memory coalescing
    // This requires R_BLOCK_J to be equal to BLOCK
    c_B_glb.store_in(&b_B_glb, {k0 * BLOCK + j1, j0 * R_BLOCK_J + i0 % BLOCK});
    c_B_glb_to_shr_pre.store_in(&b_B_shr, {0, j1, j0 % BLOCK * R_BLOCK_J + i0 % BLOCK});
    c_B_glb_to_shr.store_in(&b_B_shr, {(k0_skiplast + 1) % 2, j1, j0 % BLOCK * R_BLOCK_J + i0 % BLOCK});
    c_B_shr.store_in(&b_B_shr, {k0 % 2, k1, j0 % BLOCK * R_BLOCK_J + j1});
    c_B_shr_to_reg.store_in(&b_B_reg, {j1});
    c_A.store_in(&b_A_reg, {i % R_BLOCK_I});
    c_B.store_in(&b_B_reg, {j % R_BLOCK_J});
    c_acc_init.store_in(&b_acc, {i % R_BLOCK_I, j % R_BLOCK_J});
    c_acc.store_in(&b_acc, {i % R_BLOCK_I, j % R_BLOCK_J});
    c_C.store_in(&b_C_glb);
    c_Consts.store_in(&b_Consts, {i});

    // -------------------------------------------------------
    // Code Generation
    // -------------------------------------------------------

    // Generate object files. Last argument triggers cuda compilation.
    tiramisu::codegen({&b_Consts, &b_A, &b_B, &b_C, time_start.get_buffer(), time_end.get_buffer()}, "fct.o", true);

    return 0;
}
<commit_msg>CUDA iterator interchange - halfway 2<commit_after>#include <tiramisu/tiramisu.h>
#include <tiramisu/block.h>

#include "configuration.h"

using namespace tiramisu;

int main(int argc, char **argv)
{
    // Double tiling with Register and Shared memory
    // Fused A_reg and non-square tiling

    tiramisu::init("matmul");

    // -------------------------------------------------------
    // Layer I
    // -------------------------------------------------------

    // Declare loop iterators
    var i("i", 0, M), j("j", 0, N), k("k", 0, K);
    var i0("i0", 0, M / R_BLOCK_I), i1("i1", 0, R_BLOCK_I), j0("j0", 0, N / R_BLOCK_J), j1("j1", 0, R_BLOCK_J), k0("k0", 0, K / BLOCK), k1("k1", 0, BLOCK);
    var k0_skiplast("k0", 0, K / BLOCK - 1);
    var i00("i00"), i01("i01"), j00("j00"), j01("j01");

    // Declare cpu buffers.
    buffer b_A("b_A", {M, K}, DATA_PTYPE, a_input);
    buffer b_B("b_B", {K, N}, DATA_PTYPE, a_input);
    buffer b_C("b_C", {M, N}, DATA_PTYPE, a_output);
    buffer b_Consts("b_Consts", {2}, DATA_PTYPE, a_input);
    // Declare gpu buffers.
    buffer b_A_glb("b_A_glb", {M, K}, DATA_PTYPE, a_temporary);
    buffer b_B_glb("b_B_glb", {K, N}, DATA_PTYPE, a_temporary);
    buffer b_C_glb("b_C_glb", {M, N}, DATA_PTYPE, a_temporary);
    // "+ 1" to reduce shared memory bank conflicts
    buffer b_A_shr("b_A_shr", {2, BLOCK, BLOCK * R_BLOCK_I + 1}, DATA_PTYPE, a_temporary);
    buffer b_B_shr("b_B_shr", {2, BLOCK, BLOCK * R_BLOCK_J}, DATA_PTYPE, a_temporary);
    buffer b_A_reg("b_A_reg", {1}, DATA_PTYPE, a_temporary);
    buffer b_B_reg("b_B_reg", {R_BLOCK_J}, DATA_PTYPE, a_temporary);
    buffer b_acc("b_acc", {R_BLOCK_I, R_BLOCK_J}, DATA_PTYPE, a_temporary);
    b_A_glb.tag_gpu_global();
    b_B_glb.tag_gpu_global();
    b_C_glb.tag_gpu_global();
    b_A_shr.tag_gpu_shared();
    b_B_shr.tag_gpu_shared();
    b_A_reg.tag_gpu_register();
    b_B_reg.tag_gpu_local();
    b_acc.tag_gpu_local();

    // Declare input wrappers
    input c_A_glb({i0, j0, k0, i1}, DATA_PTYPE);
    input c_A_shr({i0, j0, k0, k1, i1}, DATA_PTYPE);
    input c_A({i, k}, DATA_PTYPE);
    input c_B_glb({i0, j0, k0, j1}, DATA_PTYPE);
    input c_B_shr({i0, j0, k0, k1, j1}, DATA_PTYPE);
    input c_B({k, j}, DATA_PTYPE);
    input c_Consts({i}, DATA_PTYPE);
    constant c_alpha("alpha", c_Consts(0));
    constant c_beta("beta", c_Consts(1));
    // Declare computations
    computation c_A_glb_to_shr_pre({i0, j0, i1}, c_A_glb(i0, j0, 0, i1));
    computation c_A_glb_to_shr({i0, j0, k0_skiplast, i1}, c_A_glb(i0, j0, k0_skiplast + 1, i1));
    computation c_A_shr_to_reg({i0, j0, k0, k1, i1}, c_A_shr(i0, j0, k0, k1, i1));
    computation c_B_glb_to_shr_pre({i0, j0, j1}, c_B_glb(i0, j0, 0, j1));
    computation c_B_glb_to_shr({i0, j0, k0_skiplast, j1}, c_B_glb(i0, j0, k0_skiplast + 1, j1));
    computation c_B_shr_to_reg({i0, j0, k0, k1, j1}, c_B_shr(i0, j0, k0, k1, j1));
    computation c_acc_init({i, j}, (float) 0);
    computation c_acc({i, j, k}, DATA_PTYPE);
    c_acc.set_expression(c_acc(i, j, 0) + c_A(i, k) * c_B(k, j));
    computation c_C({i, j}, DATA_PTYPE);
    c_C.set_expression(c_acc(i, j, 0) * c_alpha + c_C(i, j) * c_beta);
    // Declare declarations
    computation c_A_shr_dec({i0, j0}, allocate(b_A_shr));
    computation c_A_reg_dec({i0, j0}, allocate(b_A_reg));
    computation c_B_shr_dec({i0, j0}, allocate(b_B_shr));
    computation c_B_reg_dec({i0, j0}, allocate(b_B_reg));
    computation c_acc_dec({i0, j0}, allocate(b_acc));
    // Declare synchronizer computations
    computation c_sync1({i0, j0}, tiramisu::sync());
    computation c_sync2({i0, j0, k0}, tiramisu::sync());
    // Declare host-gpu transfer computations.
    computation copy_A_to_device({}, memcpy(b_A, b_A_glb));
    computation copy_B_to_device({}, memcpy(b_B, b_B_glb));
    computation copy_C_to_device({}, memcpy(b_C, b_C_glb));
    computation copy_C_to_host({}, memcpy(b_C_glb, b_C));

    computation time_start({var("dummy", 0, 1)}, expr(o_call, "get_time", {int32_t(0)}, p_float32));
    computation time_end({var("dummy", 0, 1)}, expr(o_call, "get_time", {int32_t(0)}, p_float32));
    // Synchronize before timing
    computation final_sync({var("dummy", 0, 1)}, cuda_stream_synchronize());

    // -------------------------------------------------------
    // Layer II
    // -------------------------------------------------------

    // Scheduling commands

    c_acc_init.tile(i, j, R_BLOCK_I, R_BLOCK_J, i0, j0, i1, j1);
    c_acc.tile(i, j, R_BLOCK_I, R_BLOCK_J, i0, j0, i1, j1);
    c_acc.interchange(j1, k);
    c_acc.interchange(i1, k);
    c_acc.split(k, BLOCK, k0, k1);
    c_C.tile(i, j, R_BLOCK_I, R_BLOCK_J, i0, j0, i1, j1);

    block kernel_block({&c_acc_dec, &c_acc_init, &c_acc, &c_C,
            &c_A_shr_dec, &c_A_reg_dec, &c_B_shr_dec, &c_B_reg_dec,
            &c_A_glb_to_shr_pre, &c_A_glb_to_shr, &c_A_shr_to_reg,
            &c_B_glb_to_shr_pre, &c_B_glb_to_shr, &c_B_shr_to_reg,
            &c_sync1, &c_sync2});
    kernel_block.gpu_tile(i0, j0, BLOCK, BLOCK, i00, j00, i01, j01);

    copy_A_to_device.then(copy_B_to_device, computation::root)
                    .then(copy_C_to_device, computation::root)
                    .then(time_start, computation::root)
                    .then(c_A_shr_dec, computation::root)
                    .then(c_B_shr_dec, j01)
                    .then(c_A_reg_dec, j01)
                    .then(c_B_reg_dec, j01)
                    .then(c_acc_dec, j01)
                    .then(c_acc_init, j01)
                    .then(c_A_glb_to_shr_pre, j01)
                    .then(c_B_glb_to_shr_pre, j01)
                    .then(c_sync1, j01)
                    .then(c_A_glb_to_shr, j01)
                    .then(c_B_glb_to_shr, k0)
                    .then(c_B_shr_to_reg, k0)
                    .then(c_A_shr_to_reg, k1)
                    .then(c_acc, i1)
                    .then(c_sync2, k0)
                    .then(c_C, j01)
                    .then(final_sync, computation::root)
                    .then(time_end, computation::root)
                    .then(copy_C_to_host, computation::root);

    // -------------------------------------------------------
    // Layer III
    // -------------------------------------------------------

    c_A_glb.store_in(&b_A_glb, {i0 * R_BLOCK_I + i1, k0 * BLOCK + j0 % BLOCK});
    // Note the transpose:
    c_A_glb_to_shr_pre.store_in(&b_A_shr, {0, j0 % BLOCK, i0 % BLOCK * R_BLOCK_I + i1});
    c_A_glb_to_shr.store_in(&b_A_shr, {(k0_skiplast + 1) % 2, j0 % BLOCK, i0 % BLOCK * R_BLOCK_I + i1});
    c_A_shr.store_in(&b_A_shr, {k0 % 2, k1, i0 % BLOCK * R_BLOCK_I + i1});
    c_A_shr_to_reg.store_in(&b_A_reg, {0});
    // Note that we use a transposed mapping to assure memory coalescing
    // This requires R_BLOCK_J to be equal to BLOCK
    c_B_glb.store_in(&b_B_glb, {k0 * BLOCK + j1, (j0 - j0 % BLOCK + i0 % BLOCK) * R_BLOCK_J + j0 % BLOCK});
    c_B_glb_to_shr_pre.store_in(&b_B_shr, {0, j1, i0 % BLOCK * R_BLOCK_J + j0 % BLOCK});
    c_B_glb_to_shr.store_in(&b_B_shr, {(k0_skiplast + 1) % 2, j1, i0 % BLOCK * R_BLOCK_J + j0 % BLOCK});
    c_B_shr.store_in(&b_B_shr, {k0 % 2, k1, j0 % BLOCK * R_BLOCK_J + j1});
    c_B_shr_to_reg.store_in(&b_B_reg, {j1});
    c_A.store_in(&b_A_reg, {i % R_BLOCK_I});
    c_B.store_in(&b_B_reg, {j % R_BLOCK_J});
    c_acc_init.store_in(&b_acc, {i % R_BLOCK_I, j % R_BLOCK_J});
    c_acc.store_in(&b_acc, {i % R_BLOCK_I, j % R_BLOCK_J});
    c_C.store_in(&b_C_glb);
    c_Consts.store_in(&b_Consts, {i});

    // -------------------------------------------------------
    // Code Generation
    // -------------------------------------------------------

    // Generate object files. Last argument triggers cuda compilation.
    tiramisu::codegen({&b_Consts, &b_A, &b_B, &b_C, time_start.get_buffer(), time_end.get_buffer()}, "fct.o", true);

    return 0;
}
<|endoftext|>


<commit_before>// Copyright 2018 Global Phasing Ltd.
//
// Part of gemmi-validate that does extra validation for cif files from
// the Refmac monomer dictionary ("gemmi validate --monomer").

#include <stdio.h>
#include <stdexcept>
#include "gemmi/cifdoc.hpp"
#include "gemmi/chemcomp.hpp"     // for ChemComp
#include "gemmi/chemcomp_xyz.hpp" // for make_residue_from_chemcomp_block
#include "gemmi/topo.hpp"         // for Topo
#include "gemmi/calculate.hpp"    // for calculate_angle

namespace cif = gemmi::cif;
using gemmi::Restraints;
using gemmi::Topo;

// some rules for the number of bonds (currently only for H and P)
static void check_valency(const gemmi::ChemComp& cc) {
  const std::string tag = cc.name + " [valency]";
  for (const gemmi::ChemComp::Atom& atom : cc.atoms) {
    if (cc.atoms.size() == 1)
      continue;
    float valency = 0.0f;
    for (const Restraints::Bond& bond : cc.rt.bonds)
      if (bond.id1 == atom.id || bond.id2 == atom.id)
        valency += order_of_bond_type(bond.type);
    bool ok = valency >= 0.5f;
    valency -= atom.charge;
    if (atom.is_hydrogen()) {
      ok = std::round(valency) == 1.0;
    } else if (atom.el == gemmi::El::P) {
      ok = (valency == 3.0f || valency == 5.0f || valency == 5.5f);
    }
    if (!ok)
      printf("%s %s (%s) has bond order %g\n", tag.c_str(),
             atom.id.c_str(), element_name(atom.el), valency);
  }
}

static void check_bond_angle_consistency(const gemmi::ChemComp& cc) {
  const std::string tag = cc.name + " [restr]";
  for (const Restraints::Angle& angle : cc.rt.angles) {
    if (!cc.rt.are_bonded(angle.id1, angle.id2) ||
        !cc.rt.are_bonded(angle.id2, angle.id3))
      printf("%s angle %s with non-bonded atoms\n", tag.c_str(),
             angle.str().c_str());
    if (angle.value < 20)
      printf("%s angle %s with low value: %g\n", tag.c_str(),
             angle.str().c_str(), angle.value);
  }
  for (const Restraints::Torsion& tor : cc.rt.torsions) {
    if (!cc.rt.are_bonded(tor.id1, tor.id2) ||
        !cc.rt.are_bonded(tor.id2, tor.id3) ||
        !cc.rt.are_bonded(tor.id3, tor.id4))
      printf("%s torsion %s with non-bonded atoms\n", tag.c_str(),
             tor.str().c_str());
  }
}

void print_outliers(const Topo& topo, const char* tag) {
  const double esd_mult = 2.0;
  for (const Topo::Bond& t : topo.bonds) {
    double value = t.calculate();
    if (std::abs(value - t.restr->value) > esd_mult * t.restr->esd)
      printf("%s bond %s should be %g (esd %g) but is %.2f\n", tag,
             t.restr->str().c_str(), t.restr->value, t.restr->esd, value);
  }
  for (const Topo::Angle& t : topo.angles) {
    double value = gemmi::deg(t.calculate());
    if (gemmi::angle_abs_diff(value, t.restr->value) > esd_mult * t.restr->esd)
      printf("%s angle %s should be %g (esd %g) but is %.2f\n", tag,
             t.restr->str().c_str(), t.restr->value, t.restr->esd, value);
  }
  for (const Topo::Torsion& t : topo.torsions) {
    double value = gemmi::deg(t.calculate());
    if (gemmi::angle_abs_diff(value, t.restr->value) > esd_mult * t.restr->esd)
      printf("%s torsion %s should be %g (esd %g) but is %.2f\n", tag,
             t.restr->str().c_str(), t.restr->value, t.restr->esd, value);
  }
  for (const Topo::Chirality& t : topo.chirs) {
    double value = t.calculate();
    if (t.restr->is_wrong(value))
      printf("%s chir %s should be %s but is %.2f\n", tag,
             t.restr->str().c_str(), gemmi::chirality_to_string(t.restr->sign),
             value);
  }
  for (const Topo::Plane& t : topo.planes) {
    auto coeff = find_best_plane(t.atoms);
    for (const gemmi::Atom* atom : t.atoms) {
      double dist = gemmi::get_distance_from_plane(atom->pos, coeff);
      if (dist > esd_mult * t.restr->esd)
        printf("%s plane %s has atom %s in a distance %.2f\n", tag,
               t.restr->str().c_str(), atom->name.c_str(), dist);
    }
  }
}

static void check_xyz_consistency(const gemmi::ChemComp& cc,
                                  gemmi::Residue& res) {
  Topo topo;
  topo.chain_infos.resize(1);
  Topo::ResInfo ri(&res);
  ri.chemcomp = cc;
  topo.apply_internal_restraints_to_residue(ri);
  topo.chain_infos[0].res_infos.push_back(ri);
  print_outliers(topo, (cc.name + " [atom.xyz]").c_str());
}

void check_monomer_doc(const cif::Document& doc) {
  for (const cif::Block& block : doc.blocks)
    if (block.name != "comp_list") {
      try {
        gemmi::ChemComp cc = gemmi::make_chemcomp_from_block(block);
        check_valency(cc);
        check_bond_angle_consistency(cc);
        // check consistency of _chem_comp_atom.x/y/z with restraints
        gemmi::Residue res = gemmi::make_residue_from_chemcomp_block(block,
                                                    gemmi::ChemCompModel::Xyz);
        check_xyz_consistency(cc, res);
      } catch (const std::exception& e) {
        fprintf(stderr, "Failed to interpret %s from %s:\n %s\n",
                block.name.c_str(), doc.source.c_str(), e.what());
      }
    }
}

// vim:sw=2:ts=2:et:path^=../include,../third_party
<commit_msg>gemmi-validate --monomer: fix #134 (related to #94)<commit_after>// Copyright 2018 Global Phasing Ltd.
//
// Part of gemmi-validate that does extra validation for cif files from
// the Refmac monomer dictionary ("gemmi validate --monomer").

#include <stdio.h>
#include <stdexcept>
#include "gemmi/cifdoc.hpp"
#include "gemmi/chemcomp.hpp"     // for ChemComp
#include "gemmi/chemcomp_xyz.hpp" // for make_residue_from_chemcomp_block
#include "gemmi/topo.hpp"         // for Topo
#include "gemmi/calculate.hpp"    // for calculate_angle

namespace cif = gemmi::cif;
using gemmi::Restraints;
using gemmi::Topo;

// some rules for the number of bonds (currently only for H and P)
static void check_valency(const gemmi::ChemComp& cc) {
  const std::string tag = cc.name + " [valency]";
  for (const gemmi::ChemComp::Atom& atom : cc.atoms) {
    if (cc.atoms.size() == 1)
      continue;
    float valency = 0.0f;
    for (const Restraints::Bond& bond : cc.rt.bonds)
      if (bond.id1 == atom.id || bond.id2 == atom.id)
        valency += order_of_bond_type(bond.type);
    bool ok = valency >= 0.5f;
    valency -= atom.charge;
    if (atom.is_hydrogen()) {
      ok = std::round(valency) == 1.0;
    } else if (atom.el == gemmi::El::P) {
      ok = (valency == 3.0f || valency == 5.0f || valency == 5.5f);
    }
    if (!ok)
      printf("%s %s (%s) has bond order %g\n", tag.c_str(),
             atom.id.c_str(), element_name(atom.el), valency);
  }
}

static void check_bond_angle_consistency(const gemmi::ChemComp& cc) {
  const std::string tag = cc.name + " [restr]";
  for (const Restraints::Angle& angle : cc.rt.angles) {
    if (!cc.rt.are_bonded(angle.id1, angle.id2) ||
        !cc.rt.are_bonded(angle.id2, angle.id3))
      printf("%s angle %s with non-bonded atoms\n", tag.c_str(),
             angle.str().c_str());
    if (angle.value < 20)
      printf("%s angle %s with low value: %g\n", tag.c_str(),
             angle.str().c_str(), angle.value);
  }
  for (const Restraints::Torsion& tor : cc.rt.torsions) {
    if (!cc.rt.are_bonded(tor.id1, tor.id2) ||
        !cc.rt.are_bonded(tor.id2, tor.id3) ||
        !cc.rt.are_bonded(tor.id3, tor.id4))
      printf("%s torsion %s with non-bonded atoms\n", tag.c_str(),
             tor.str().c_str());
  }
}

void print_outliers(const Topo& topo, const char* tag) {
  const double esd_mult = 2.0;
  for (const Topo::Bond& t : topo.bonds) {
    double value = t.calculate();
    if (std::abs(value - t.restr->value) > esd_mult * t.restr->esd)
      printf("%s bond %s should be %g (esd %g) but is %.2f\n", tag,
             t.restr->str().c_str(), t.restr->value, t.restr->esd, value);
  }
  for (const Topo::Angle& t : topo.angles) {
    double value = gemmi::deg(t.calculate());
    if (gemmi::angle_abs_diff(value, t.restr->value) > esd_mult * t.restr->esd)
      printf("%s angle %s should be %g (esd %g) but is %.2f\n", tag,
             t.restr->str().c_str(), t.restr->value, t.restr->esd, value);
  }
  for (const Topo::Torsion& t : topo.torsions) {
    double value = gemmi::deg(t.calculate());
    double full = 360. / std::max(1, t.restr->period);
    if (gemmi::angle_abs_diff(value, t.restr->value, full) > esd_mult * t.restr->esd)
      printf("%s torsion %s should be %g (esd %g) but is %.2f\n", tag,
             t.restr->str().c_str(), t.restr->value, t.restr->esd, value);
  }
  for (const Topo::Chirality& t : topo.chirs) {
    double value = t.calculate();
    if (t.restr->is_wrong(value))
      printf("%s chir %s should be %s but is %.2f\n", tag,
             t.restr->str().c_str(), gemmi::chirality_to_string(t.restr->sign),
             value);
  }
  for (const Topo::Plane& t : topo.planes) {
    auto coeff = find_best_plane(t.atoms);
    for (const gemmi::Atom* atom : t.atoms) {
      double dist = gemmi::get_distance_from_plane(atom->pos, coeff);
      if (dist > esd_mult * t.restr->esd)
        printf("%s plane %s has atom %s in a distance %.2f\n", tag,
               t.restr->str().c_str(), atom->name.c_str(), dist);
    }
  }
}

static void check_xyz_consistency(const gemmi::ChemComp& cc,
                                  gemmi::Residue& res) {
  Topo topo;
  topo.chain_infos.resize(1);
  Topo::ResInfo ri(&res);
  ri.chemcomp = cc;
  topo.apply_internal_restraints_to_residue(ri);
  topo.chain_infos[0].res_infos.push_back(ri);
  print_outliers(topo, (cc.name + " [atom.xyz]").c_str());
}

void check_monomer_doc(const cif::Document& doc) {
  for (const cif::Block& block : doc.blocks)
    if (block.name != "comp_list") {
      try {
        gemmi::ChemComp cc = gemmi::make_chemcomp_from_block(block);
        check_valency(cc);
        check_bond_angle_consistency(cc);
        // check consistency of _chem_comp_atom.x/y/z with restraints
        gemmi::Residue res = gemmi::make_residue_from_chemcomp_block(block,
                                                    gemmi::ChemCompModel::Xyz);
        check_xyz_consistency(cc, res);
      } catch (const std::exception& e) {
        fprintf(stderr, "Failed to interpret %s from %s:\n %s\n",
                block.name.c_str(), doc.source.c_str(), e.what());
      }
    }
}

// vim:sw=2:ts=2:et:path^=../include,../third_party
<|endoftext|>


<commit_before>/*
 * *****************************************************************************
 *   Copyright 2014-2015 Spectra Logic Corporation. All Rights Reserved.
 *   Licensed under the Apache License, Version 2.0 (the "License"). You may not
 *   use this file except in compliance with the License. A copy of the License
 *   is located at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *   or in the "license" file accompanying this file.
 *   This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 *   CONDITIONS OF ANY KIND, either express or implied. See the License for the
 *   specific language governing permissions and limitations under the License.
 * *****************************************************************************
 */

#include <QAction>
#include <QFile>
#include <QFileDialog>
#include <QMenu>
#include <QTextStream>

#include "lib/logger.h"
#include "views/console.h"

#ifdef NO_DEBUG
#define DEFAULT_LOG_LEVEL INFO
#else
#define DEFAULT_LOG_LEVEL DEBUG
#endif

const QString Console::LEVEL_COLORS[] = { "Blue", "Black", "darkYellow", "Red" };
const unsigned int Console::MAX_LINES = 1000;
Console* Console::s_instance = 0;

Console*
Console::Instance()
{
	if (!s_instance) {
		s_instance = new Console();
	}
	return s_instance;
}

Console::Console(QWidget* parent)
	: QWidget(parent),
	  m_logLevel(DEFAULT_LOG_LEVEL),
	  m_numLines(0)
{
	m_text = new QTextEdit();
	m_text->setReadOnly(true);
	m_text->setContextMenuPolicy(Qt::CustomContextMenu);
	connect(m_text, SIGNAL(customContextMenuRequested(const QPoint&)),
		this, SLOT(ShowContextMenu(const QPoint&)));
	m_layout = new QVBoxLayout(this);
	m_layout->addWidget(m_text);
	setLayout(m_layout);

	connect(this, SIGNAL(MessageReadyToLog(int, const QString&)),
		this, SLOT(LogPrivate(int, const QString&)));
}

void
Console::Log(Level level, const QString& msg)
{
	if (level < m_logLevel) {
		return;
	}

	emit MessageReadyToLog(level, msg);
}

void
Console::LogPrivate(int level, const QString& msg)
{
	QString fullMsg(msg);

	QColor color;
	switch (level) {
	case DEBUG:
		color = QColor("Blue");
		break;
	case INFO:
		color = QColor("Black");
		break;
	case WARNING:
		color = QColor(175, 175, 0);
		break;
	case ERR:
		color = QColor("Red");
		break;
	};

	m_lock.lock();
	if (m_numLines >= MAX_LINES) {
		m_text->moveCursor(QTextCursor::Start,
				   QTextCursor::MoveAnchor);
		m_text->moveCursor(QTextCursor::Down,
				   QTextCursor::KeepAnchor);
		m_text->moveCursor(QTextCursor::StartOfLine,
				   QTextCursor::KeepAnchor);
		m_text->textCursor().removeSelectedText();
		m_numLines--;
	}
	m_text->moveCursor(QTextCursor::End,
			   QTextCursor::MoveAnchor);
	m_text->setTextColor(color);
	m_text->append(fullMsg);
	m_numLines++;
	m_text->ensureCursorVisible();
	m_lock.unlock();
}

void
Console::ShowContextMenu(const QPoint& /*pos*/)
{
	QMenu* menu = m_text->createStandardContextMenu();
	menu->addSeparator();
	QAction save("Save As...", menu);
	menu->addAction(&save);
	QAction* selectedAction = menu->exec(QCursor::pos());

	if (selectedAction == &save) {
		SaveToFile();
	}
}

void
Console::SaveToFile()
{
	QString fileName = QFileDialog::getSaveFileName(this,
							"Save Log",
							QDir::homePath());
	QFile file(fileName);
	if (file.open(QIODevice::ReadWrite)) {
		QTextStream stream(&file);
		stream << m_text->toPlainText() << endl;
		file.flush();
		file.close();
		LOG_INFO("Saved log to " + fileName);
	} else {
		LOG_ERROR("Unable to save log to " + fileName);
	}
}
<commit_msg>Don't explicitely set the text color for info level logs<commit_after>/*
 * *****************************************************************************
 *   Copyright 2014-2015 Spectra Logic Corporation. All Rights Reserved.
 *   Licensed under the Apache License, Version 2.0 (the "License"). You may not
 *   use this file except in compliance with the License. A copy of the License
 *   is located at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *   or in the "license" file accompanying this file.
 *   This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 *   CONDITIONS OF ANY KIND, either express or implied. See the License for the
 *   specific language governing permissions and limitations under the License.
 * *****************************************************************************
 */

#include <QAction>
#include <QFile>
#include <QFileDialog>
#include <QMenu>
#include <QTextStream>

#include "lib/logger.h"
#include "views/console.h"

#ifdef NO_DEBUG
#define DEFAULT_LOG_LEVEL INFO
#else
#define DEFAULT_LOG_LEVEL DEBUG
#endif

const QString Console::LEVEL_COLORS[] = { "Blue", "Black", "darkYellow", "Red" };
const unsigned int Console::MAX_LINES = 1000;
Console* Console::s_instance = 0;

Console*
Console::Instance()
{
	if (!s_instance) {
		s_instance = new Console();
	}
	return s_instance;
}

Console::Console(QWidget* parent)
	: QWidget(parent),
	  m_logLevel(DEFAULT_LOG_LEVEL),
	  m_numLines(0)
{
	m_text = new QTextEdit();
	m_text->setReadOnly(true);
	m_text->setContextMenuPolicy(Qt::CustomContextMenu);
	connect(m_text, SIGNAL(customContextMenuRequested(const QPoint&)),
		this, SLOT(ShowContextMenu(const QPoint&)));
	m_layout = new QVBoxLayout(this);
	m_layout->addWidget(m_text);
	setLayout(m_layout);

	connect(this, SIGNAL(MessageReadyToLog(int, const QString&)),
		this, SLOT(LogPrivate(int, const QString&)));
}

void
Console::Log(Level level, const QString& msg)
{
	if (level < m_logLevel) {
		return;
	}

	emit MessageReadyToLog(level, msg);
}

void
Console::LogPrivate(int level, const QString& msg)
{
	QString color;
	switch (level) {
	case DEBUG:
		color = "blue";
		break;
	case WARNING:
		color = QColor(175, 175, 0).name();
		break;
	case ERR:
		color = "red";
		break;
	};

	m_lock.lock();
	if (m_numLines >= MAX_LINES) {
		m_text->moveCursor(QTextCursor::Start,
				   QTextCursor::MoveAnchor);
		m_text->moveCursor(QTextCursor::Down,
				   QTextCursor::KeepAnchor);
		m_text->moveCursor(QTextCursor::StartOfLine,
				   QTextCursor::KeepAnchor);
		m_text->textCursor().removeSelectedText();
		m_numLines--;
	}
	m_text->moveCursor(QTextCursor::End,
			   QTextCursor::MoveAnchor);
	QString html = "" + msg + "
";
	m_text->insertHtml(html);
	m_numLines++;
	m_text->ensureCursorVisible();
	m_lock.unlock();
}

void
Console::ShowContextMenu(const QPoint& /*pos*/)
{
	QMenu* menu = m_text->createStandardContextMenu();
	menu->addSeparator();
	QAction save("Save As...", menu);
	menu->addAction(&save);
	QAction* selectedAction = menu->exec(QCursor::pos());

	if (selectedAction == &save) {
		SaveToFile();
	}
}

void
Console::SaveToFile()
{
	QString fileName = QFileDialog::getSaveFileName(this,
							"Save Log",
							QDir::homePath());
	QFile file(fileName);
	if (file.open(QIODevice::ReadWrite)) {
		QTextStream stream(&file);
		stream << m_text->toPlainText() << endl;
		file.flush();
		file.close();
		LOG_INFO("Saved log to " + fileName);
	} else {
		LOG_ERROR("Unable to save log to " + fileName);
	}
}
<|endoftext|>


<commit_before>//This file must compile on ALL PLATFORMS. Be very careful what you put in here.
#include "HAL/HAL.hpp"
#include "FRC_NetworkCommunication/FRCComm.h"
#include <cstring>

extern "C" {

int HALGetControlWord(HALControlWord *data)
{
	return FRC_NetworkCommunication_getControlWord((ControlWord_t*) data);
}

void HALSetNewDataSem(MULTIWAIT_ID sem)
{
	setNewDataSem(sem->native_handle());
}

int HALGetAllianceStation(enum HALAllianceStationID *allianceStation)
{
	return FRC_NetworkCommunication_getAllianceStation((AllianceStationID_t*) allianceStation);
}

int HALGetJoystickAxes(uint8_t joystickNum, HALJoystickAxes *axes)
{
	return FRC_NetworkCommunication_getJoystickAxes(joystickNum, (JoystickAxes_t*) axes, kMaxJoystickAxes);
}

int HALGetJoystickPOVs(uint8_t joystickNum, HALJoystickPOVs *povs)
{
	return FRC_NetworkCommunication_getJoystickPOVs(joystickNum, (JoystickPOV_t*) povs, kMaxJoystickPOVs);
}

int HALGetJoystickButtons(uint8_t joystickNum, HALJoystickButtons *buttons)
{
	return FRC_NetworkCommunication_getJoystickButtons(joystickNum, &buttons->buttons, &buttons->count);
}

int HALGetJoystickDescriptor(uint8_t joystickNum, HALJoystickDescriptor *desc)
{
	desc->isXbox = 0;
	desc->type = -1;
	desc->name[0] = '\0';
	desc->axisCount = 0;
	desc->buttonCount = 0;
	desc->povCount = 0;
	return FRC_NetworkCommunication_getJoystickDesc(joystickNum, &desc->isXbox, &desc->type, (char *)(&desc->name),
		&desc->axisCount, (uint8_t *)&desc->axisTypes, &desc->buttonCount, &desc->povCount);
}

int HALGetJoystickIsXbox(uint8_t joystickNum)
{
	HALJoystickDescriptor joystickDesc;
	if(HALGetJoystickDescriptor(joystickNum, &joystickDesc)<0)
	{
		return 0;
	}else
	{
	return joystickDesc.isXbox;
	}
}

int HALGetJoystickType(uint8_t joystickNum)
{
	HALJoystickDescriptor joystickDesc;
	if(HALGetJoystickDescriptor(joystickNum, &joystickDesc)<0)
	{
		return -1;
	} else
	{
	return joystickDesc.type;
	}
}

char* HALGetJoystickName(uint8_t joystickNum)
{
	HALJoystickDescriptor joystickDesc;
	if(HALGetJoystickDescriptor(joystickNum, &joystickDesc)<0)
	{
		char* name = (char*)std::malloc(1);
		name[0] = '\0';
		return name;
	} else
	{
		size_t len = std::strlen(joystickDesc.name);
		char* name = (char*)std::malloc(len+1);
		std::strcpy(name, joystickDesc.name);
		return name;
	}
}

int HALGetJoystickAxisType(uint8_t joystickNum, uint8_t axis)
{
	HALJoystickDescriptor joystickDesc;
	if(HALGetJoystickDescriptor(joystickNum, &joystickDesc)<0)
	{
		return -1;
	} else
	{
	return joystickDesc.axisTypes[axis];
	}
}

int HALSetJoystickOutputs(uint8_t joystickNum, uint32_t outputs, uint16_t leftRumble, uint16_t rightRumble)
{
	return FRC_NetworkCommunication_setJoystickOutputs(joystickNum, outputs, leftRumble, rightRumble);
}

int HALGetMatchTime(float *matchTime)
{
	return FRC_NetworkCommunication_getMatchTime(matchTime);
}

void HALNetworkCommunicationObserveUserProgramStarting(void)
{
	FRC_NetworkCommunication_observeUserProgramStarting();
}

void HALNetworkCommunicationObserveUserProgramDisabled(void)
{
	FRC_NetworkCommunication_observeUserProgramDisabled();
}

void HALNetworkCommunicationObserveUserProgramAutonomous(void)
{
	FRC_NetworkCommunication_observeUserProgramAutonomous();
}

void HALNetworkCommunicationObserveUserProgramTeleop(void)
{
	FRC_NetworkCommunication_observeUserProgramTeleop();
}

void HALNetworkCommunicationObserveUserProgramTest(void)
{
	FRC_NetworkCommunication_observeUserProgramTest();
}

}  // extern "C"
<commit_msg>Another improvement to HAL-joy getting to ensure it works in future RIO image updates.<commit_after>//This file must compile on ALL PLATFORMS. Be very careful what you put in here.
#include "HAL/HAL.hpp"
#include "FRC_NetworkCommunication/FRCComm.h"
#include <cstring>

extern "C" {

int HALGetControlWord(HALControlWord *data)
{
	return FRC_NetworkCommunication_getControlWord((ControlWord_t*) data);
}

void HALSetNewDataSem(MULTIWAIT_ID sem)
{
	setNewDataSem(sem->native_handle());
}

int HALGetAllianceStation(enum HALAllianceStationID *allianceStation)
{
	return FRC_NetworkCommunication_getAllianceStation((AllianceStationID_t*) allianceStation);
}

int HALGetJoystickAxes(uint8_t joystickNum, HALJoystickAxes *axes)
{
	return FRC_NetworkCommunication_getJoystickAxes(joystickNum, (JoystickAxes_t*) axes, kMaxJoystickAxes);
}

int HALGetJoystickPOVs(uint8_t joystickNum, HALJoystickPOVs *povs)
{
	return FRC_NetworkCommunication_getJoystickPOVs(joystickNum, (JoystickPOV_t*) povs, kMaxJoystickPOVs);
}

int HALGetJoystickButtons(uint8_t joystickNum, HALJoystickButtons *buttons)
{
	return FRC_NetworkCommunication_getJoystickButtons(joystickNum, &buttons->buttons, &buttons->count);
}
/**
 * Retrieve the Joystick Descriptor for particular slot
 * @param desc [out] descriptor (data transfer object) to fill in.  desc is filled in regardless of success.
 *                              In other words, if descriptor is not available, desc is filled in with default
 *                              values matching the init-values in Java and C++ Driverstation for when caller
 *                              requests a too-large joystick index.
 *
 * @return error code reported from Network Comm back-end.  Zero is good, nonzero is bad.
 */
int HALGetJoystickDescriptor(uint8_t joystickNum, HALJoystickDescriptor *desc)
{
	desc->isXbox = 0;
	desc->type = -1;
	desc->name[0] = '\0';
	desc->axisCount = kMaxJoystickAxes; /* set to the desc->axisTypes's capacity */
	desc->buttonCount = 0;
	desc->povCount = 0;
	int retval = FRC_NetworkCommunication_getJoystickDesc(joystickNum, &desc->isXbox, &desc->type, (char *)(&desc->name),
		&desc->axisCount, (uint8_t *)&desc->axisTypes, &desc->buttonCount, &desc->povCount);
	/* check the return, if there is an error and the RIOimage predates FRC2017, then axisCount needs to be cleared */
	if(retval != 0)
	{
		/* set count to zero so downstream code doesn't decode invalid axisTypes. */
		desc->axisCount = 0;
	}
	return retval;
}

int HALGetJoystickIsXbox(uint8_t joystickNum)
{
	HALJoystickDescriptor joystickDesc;
	if(HALGetJoystickDescriptor(joystickNum, &joystickDesc)<0)
	{
		return 0;
	}else
	{
	return joystickDesc.isXbox;
	}
}

int HALGetJoystickType(uint8_t joystickNum)
{
	HALJoystickDescriptor joystickDesc;
	if(HALGetJoystickDescriptor(joystickNum, &joystickDesc)<0)
	{
		return -1;
	} else
	{
	return joystickDesc.type;
	}
}

char* HALGetJoystickName(uint8_t joystickNum)
{
	HALJoystickDescriptor joystickDesc;
	if(HALGetJoystickDescriptor(joystickNum, &joystickDesc)<0)
	{
		char* name = (char*)std::malloc(1);
		name[0] = '\0';
		return name;
	} else
	{
		size_t len = std::strlen(joystickDesc.name);
		char* name = (char*)std::malloc(len+1);
		std::strcpy(name, joystickDesc.name);
		return name;
	}
}

int HALGetJoystickAxisType(uint8_t joystickNum, uint8_t axis)
{
	HALJoystickDescriptor joystickDesc;
	if(HALGetJoystickDescriptor(joystickNum, &joystickDesc)<0)
	{
		return -1;
	} else
	{
	return joystickDesc.axisTypes[axis];
	}
}

int HALSetJoystickOutputs(uint8_t joystickNum, uint32_t outputs, uint16_t leftRumble, uint16_t rightRumble)
{
	return FRC_NetworkCommunication_setJoystickOutputs(joystickNum, outputs, leftRumble, rightRumble);
}

int HALGetMatchTime(float *matchTime)
{
	return FRC_NetworkCommunication_getMatchTime(matchTime);
}

void HALNetworkCommunicationObserveUserProgramStarting(void)
{
	FRC_NetworkCommunication_observeUserProgramStarting();
}

void HALNetworkCommunicationObserveUserProgramDisabled(void)
{
	FRC_NetworkCommunication_observeUserProgramDisabled();
}

void HALNetworkCommunicationObserveUserProgramAutonomous(void)
{
	FRC_NetworkCommunication_observeUserProgramAutonomous();
}

void HALNetworkCommunicationObserveUserProgramTeleop(void)
{
	FRC_NetworkCommunication_observeUserProgramTeleop();
}

void HALNetworkCommunicationObserveUserProgramTest(void)
{
	FRC_NetworkCommunication_observeUserProgramTest();
}

}  // extern "C"
<|endoftext|>