<commit_before>#include "MacroLex.h"
#include "CmdInputFactor.h"
#include <gtest/gtest.h>
#include "JZLogger.h"

//cover case : error
TEST(MacroLex, errorTest1){
  //this is really a strong test, so don't do it for more than one file every time
  int argc = 2;
  char argv0[128] = {0},argv1[128] = {0};
  strcpy(argv0,"tester");
  strcpy(argv1,"./test/TestSet/macro_test_error_1");
  char* argv[2] = {argv0,argv1};

	JZSetLoggerLevel(JZ_LOG_TEST);

	//analyze command line input
	CmdInputFactor::getInstance()->analyze(argc, argv);

	//now begin to analyze the files input from command line
	string toCompileFile = CmdInputFactor::getInstance()->getNextFile();

  MacroLex lex;
//	JZSetLoggerLevel(JZ_LOG_DEBUG);
  lex.analyzeAFile(toCompileFile);

}

//cover case : error
TEST(MacroLex, errorTest2){
  //this is really a strong test, so don't do it for more than one file every time
  int argc = 2;
  char argv0[128] = {0},argv1[128] = {0};
  strcpy(argv0,"tester");
  strcpy(argv1,"./test/TestSet/macro_test_error_2");
  char* argv[2] = {argv0,argv1};

	JZSetLoggerLevel(JZ_LOG_TEST);

	//analyze command line input
	CmdInputFactor::getInstance()->analyze(argc, argv);

	//now begin to analyze the files input from command line
	string toCompileFile = CmdInputFactor::getInstance()->getNextFile();

  MacroLex lex;
//	JZSetLoggerLevel(JZ_LOG_DEBUG);
  lex.analyzeAFile(toCompileFile);

}

//cover case : error
TEST(MacroLex, errorTest3){
  //this is really a strong test, so don't do it for more than one file every time
  int argc = 2;
  char argv0[128] = {0},argv1[128] = {0};
  strcpy(argv0,"tester");
  strcpy(argv1,"./test/TestSet/macro_test_error_3");
  char* argv[2] = {argv0,argv1};

	JZSetLoggerLevel(JZ_LOG_TEST);

	//analyze command line input
	CmdInputFactor::getInstance()->analyze(argc, argv);

	//now begin to analyze the files input from command line
	string toCompileFile = CmdInputFactor::getInstance()->getNextFile();

  MacroLex lex;
//	JZSetLoggerLevel(JZ_LOG_DEBUG);
  lex.analyzeAFile(toCompileFile);

}

//cover case : error, also var arg without other args
TEST(MacroLex, errorTest4){
  //this is really a strong test, so don't do it for more than one file every time
  int argc = 2;
  char argv0[128] = {0},argv1[128] = {0};
  strcpy(argv0,"tester");
  strcpy(argv1,"./test/TestSet/macro_test_error_4");
  char* argv[2] = {argv0,argv1};

	JZSetLoggerLevel(JZ_LOG_TEST);

	//analyze command line input
	CmdInputFactor::getInstance()->analyze(argc, argv);

	//now begin to analyze the files input from command line
	string toCompileFile = CmdInputFactor::getInstance()->getNextFile();

  MacroLex lex;
//	JZSetLoggerLevel(JZ_LOG_DEBUG);
  uint32 analyzeRet = lex.analyzeAFile(toCompileFile);

  ASSERT_EQ(analyzeRet, LexBase::eLexFuncLikeMacroParamTooManay);

  LexRecList ret = lex.getRecList();

  ASSERT_EQ(ret.size(), 8);
  ASSERT_STREQ(ret[0].word.c_str(), "hello");
  ASSERT_STREQ(ret[1].word.c_str(), "(");
  ASSERT_STREQ(ret[2].word.c_str(), "a");
  ASSERT_STREQ(ret[3].word.c_str(), ",");
  ASSERT_STREQ(ret[4].word.c_str(), "b");
  ASSERT_STREQ(ret[5].word.c_str(), ",");
  ASSERT_STREQ(ret[6].word.c_str(), "c");
  ASSERT_STREQ(ret[7].word.c_str(), ")");

}

//cover case : error
TEST(MacroLex, errorTest5){
  //this is really a strong test, so don't do it for more than one file every time
  int argc = 2;
  char argv0[128] = {0},argv1[128] = {0};
  strcpy(argv0,"tester");
  strcpy(argv1,"./test/TestSet/macro_test_error_5");
  char* argv[2] = {argv0,argv1};

	JZSetLoggerLevel(JZ_LOG_TEST);

	//analyze command line input
	CmdInputFactor::getInstance()->analyze(argc, argv);

	//now begin to analyze the files input from command line
	string toCompileFile = CmdInputFactor::getInstance()->getNextFile();

  MacroLex lex;
//	JZSetLoggerLevel(JZ_LOG_DEBUG);
  uint32 ret = lex.analyzeAFile(toCompileFile);

  ASSERT_EQ(LexBase::eLexFuncLikeMacroParamTooLess, ret);

}

//cover case : error because input without )
TEST(MacroLex, errorTest6){
  //this is really a strong test, so don't do it for more than one file every time
  int argc = 2;
  char argv0[128] = {0},argv1[128] = {0};
  strcpy(argv0,"tester");
  strcpy(argv1,"./test/TestSet/macro_test_error_6");
  char* argv[2] = {argv0,argv1};

	JZSetLoggerLevel(JZ_LOG_TEST);

	//analyze command line input
	CmdInputFactor::getInstance()->analyze(argc, argv);

	//now begin to analyze the files input from command line
	string toCompileFile = CmdInputFactor::getInstance()->getNextFile();

  MacroLex lex;
//	JZSetLoggerLevel(JZ_LOG_DEBUG);
  uint32 ret = lex.analyzeAFile(toCompileFile);

}

//cover case : error because looping define macro
TEST(MacroLex, errorTest7){
  //this is really a strong test, so don't do it for more than one file every time
  int argc = 2;
  char argv0[128] = {0},argv1[128] = {0};
  strcpy(argv0,"tester");
  strcpy(argv1,"./test/TestSet/macro_test_error_7");
  char* argv[2] = {argv0,argv1};

	JZSetLoggerLevel(JZ_LOG_TEST);

	//analyze command line input
	CmdInputFactor::getInstance()->analyze(argc, argv);

	//now begin to analyze the files input from command line
	string toCompileFile = CmdInputFactor::getInstance()->getNextFile();

  MacroLex lex;
//	JZSetLoggerLevel(JZ_LOG_DEBUG);
  uint32 ret = lex.analyzeAFile(toCompileFile);

  ASSERT_EQ(ret, LexBase::eLexMacroIsAlreadyExpending);

}
<commit_msg>update testcase<commit_after>#include "MacroLex.h"
#include "CmdInputFactor.h"
#include <gtest/gtest.h>
#include "JZLogger.h"

//cover case : error
TEST(MacroLex, errorTest1){
  //this is really a strong test, so don't do it for more than one file every time
  int argc = 2;
  char argv0[128] = {0},argv1[128] = {0};
  strcpy(argv0,"tester");
  strcpy(argv1,"./test/TestSet/macro_test_error_1");
  char* argv[2] = {argv0,argv1};

	JZSetLoggerLevel(JZ_LOG_TEST);

	//analyze command line input
	CmdInputFactor::getInstance()->analyze(argc, argv);

	//now begin to analyze the files input from command line
	string toCompileFile = CmdInputFactor::getInstance()->getNextFile();

  MacroLex lex;
//	JZSetLoggerLevel(JZ_LOG_DEBUG);
  lex.analyzeAFile(toCompileFile);

}

//cover case : error
TEST(MacroLex, errorTest2){
  //this is really a strong test, so don't do it for more than one file every time
  int argc = 2;
  char argv0[128] = {0},argv1[128] = {0};
  strcpy(argv0,"tester");
  strcpy(argv1,"./test/TestSet/macro_test_error_2");
  char* argv[2] = {argv0,argv1};

	JZSetLoggerLevel(JZ_LOG_TEST);

	//analyze command line input
	CmdInputFactor::getInstance()->analyze(argc, argv);

	//now begin to analyze the files input from command line
	string toCompileFile = CmdInputFactor::getInstance()->getNextFile();

  MacroLex lex;
//	JZSetLoggerLevel(JZ_LOG_DEBUG);
  lex.analyzeAFile(toCompileFile);

}

//cover case : error
TEST(MacroLex, errorTest3){
  //this is really a strong test, so don't do it for more than one file every time
  int argc = 2;
  char argv0[128] = {0},argv1[128] = {0};
  strcpy(argv0,"tester");
  strcpy(argv1,"./test/TestSet/macro_test_error_3");
  char* argv[2] = {argv0,argv1};

	JZSetLoggerLevel(JZ_LOG_TEST);

	//analyze command line input
	CmdInputFactor::getInstance()->analyze(argc, argv);

	//now begin to analyze the files input from command line
	string toCompileFile = CmdInputFactor::getInstance()->getNextFile();

  MacroLex lex;
//	JZSetLoggerLevel(JZ_LOG_DEBUG);
  lex.analyzeAFile(toCompileFile);

}

//cover case : error, also var arg without other args
TEST(MacroLex, errorTest4){
  //this is really a strong test, so don't do it for more than one file every time
  int argc = 2;
  char argv0[128] = {0},argv1[128] = {0};
  strcpy(argv0,"tester");
  strcpy(argv1,"./test/TestSet/macro_test_error_4");
  char* argv[2] = {argv0,argv1};

	JZSetLoggerLevel(JZ_LOG_TEST);

	//analyze command line input
	CmdInputFactor::getInstance()->analyze(argc, argv);

	//now begin to analyze the files input from command line
	string toCompileFile = CmdInputFactor::getInstance()->getNextFile();

  MacroLex lex;
//	JZSetLoggerLevel(JZ_LOG_DEBUG);
  uint32 analyzeRet = lex.analyzeAFile(toCompileFile);

  ASSERT_EQ(analyzeRet, LexBase::eLexFuncLikeMacroParamTooManay);

  LexRecList ret = lex.getRecList();

  ASSERT_EQ(ret.size(), 8);
  ASSERT_STREQ(ret[0].word.c_str(), "hello");
  ASSERT_STREQ(ret[1].word.c_str(), "(");
  ASSERT_STREQ(ret[2].word.c_str(), "a");
  ASSERT_STREQ(ret[3].word.c_str(), ",");
  ASSERT_STREQ(ret[4].word.c_str(), "b");
  ASSERT_STREQ(ret[5].word.c_str(), ",");
  ASSERT_STREQ(ret[6].word.c_str(), "c");
  ASSERT_STREQ(ret[7].word.c_str(), ")");

}

//cover case : error
TEST(MacroLex, errorTest5){
  //this is really a strong test, so don't do it for more than one file every time
  int argc = 2;
  char argv0[128] = {0},argv1[128] = {0};
  strcpy(argv0,"tester");
  strcpy(argv1,"./test/TestSet/macro_test_error_5");
  char* argv[2] = {argv0,argv1};

	JZSetLoggerLevel(JZ_LOG_TEST);

	//analyze command line input
	CmdInputFactor::getInstance()->analyze(argc, argv);

	//now begin to analyze the files input from command line
	string toCompileFile = CmdInputFactor::getInstance()->getNextFile();

  MacroLex lex;
//	JZSetLoggerLevel(JZ_LOG_DEBUG);
  uint32 ret = lex.analyzeAFile(toCompileFile);

  ASSERT_EQ(LexBase::eLexFuncLikeMacroParamTooLess, ret);

}

//cover case : error because input without )
TEST(MacroLex, errorTest6){
  //this is really a strong test, so don't do it for more than one file every time
  int argc = 2;
  char argv0[128] = {0},argv1[128] = {0};
  strcpy(argv0,"tester");
  strcpy(argv1,"./test/TestSet/macro_test_error_6");
  char* argv[2] = {argv0,argv1};

	JZSetLoggerLevel(JZ_LOG_TEST);

	//analyze command line input
	CmdInputFactor::getInstance()->analyze(argc, argv);

	//now begin to analyze the files input from command line
	string toCompileFile = CmdInputFactor::getInstance()->getNextFile();

  MacroLex lex;
//	JZSetLoggerLevel(JZ_LOG_DEBUG);
  uint32 ret = lex.analyzeAFile(toCompileFile);

}

//cover case : error because looping define macro
TEST(MacroLex, errorTest7){
  //this is really a strong test, so don't do it for more than one file every time
  int argc = 2;
  char argv0[128] = {0},argv1[128] = {0};
  strcpy(argv0,"tester");
  strcpy(argv1,"./test/TestSet/macro_test_error_7");
  char* argv[2] = {argv0,argv1};

	JZSetLoggerLevel(JZ_LOG_TEST);

	//analyze command line input
	CmdInputFactor::getInstance()->analyze(argc, argv);

	//now begin to analyze the files input from command line
	string toCompileFile = CmdInputFactor::getInstance()->getNextFile();

  MacroLex lex;
  uint32 ret = lex.analyzeAFile(toCompileFile);
	JZSetLoggerLevel(JZ_LOG_DEBUG);
  lex.printLexRec();
	JZSetLoggerLevel(JZ_LOG_TEST);

  LexRecList list = lex.getRecList();

  ASSERT_EQ(list.size() ,4);
  ASSERT_STREQ(list[0].word.c_str(), "he");
  ASSERT_STREQ(list[1].word.c_str(), "(");
  ASSERT_STREQ(list[2].word.c_str(), "a");
  ASSERT_STREQ(list[3].word.c_str(), ")");

}
<|endoftext|>


<commit_before>/* Copyright (c) 2010 Stanford University
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include "Common.h"
#include "BackupStorage.h"

namespace RAMCloud {

// --- BackupStorage ---

/**
 * Report the read and write speed of this storage in MB/s.
 *
 * \return
 *      A pair of storage read and write speeds in MB/s.
 */
pair<uint32_t, uint32_t>
BackupStorage::benchmark()
{
    const uint64_t count = 16;
    const uint64_t mb = Segment::SEGMENT_SIZE * count / 1024 / 1024;
    vector<uint32_t> readSpeeds;
    vector<uint32_t> writeSpeeds;
    BackupStorage::Handle* handles[count];

    void* p = NULL;
    int r = posix_memalign(&p, Segment::SEGMENT_SIZE, Segment::SEGMENT_SIZE);
    if (r != 0)
        throw std::bad_alloc();
    char* segment = static_cast<char*>(p);

    try {
        for (uint64_t i = 0; i < count; ++i)
            handles[i] = NULL;
        for (uint64_t i = 0; i < count; ++i)
            handles[i] = allocate(0, i + 1);

        {
            CycleCounter<> counter;
            for (uint64_t i = 0; i < count; ++i)
                putSegment(handles[i], segment);
            uint64_t ns = cyclesToNanoseconds(counter.stop());
            writeSpeeds.push_back(downCast<uint32_t>(mb * 1000 * 1000 * 1000 /
                                                     ns));
        }
        {
            CycleCounter<> counter;
            for (uint64_t i = 0; i < count; ++i)
                getSegment(handles[i], segment);
            uint64_t ns = cyclesToNanoseconds(counter.stop());
            readSpeeds.push_back(downCast<uint32_t>(mb * 1000 * 1000 * 1000 /
                                                    ns));
        }
    } catch (...) {
        std::free(segment);
        for (uint64_t i = 0; i < 16; ++i)
            if (handles[i])
                free(handles[i]);
        throw;
    }

    std::free(segment);
    for (uint64_t i = 0; i < 16; ++i)
        free(handles[i]);

    uint32_t minRead = *std::min_element(readSpeeds.begin(),
                                         readSpeeds.end());
    uint32_t minWrite = *std::min_element(writeSpeeds.begin(),
                                          writeSpeeds.end());

    LOG(NOTICE, "Backup storage speeds (min): %u MB/s read, %u MB/s write",
        minRead, minWrite);

    return {minRead, minWrite};
}

// --- BackupStorage::Handle ---

int32_t BackupStorage::Handle::allocatedHandlesCount = 0;

// --- SingleFileStorage ---

// - public -

/**
 * Create a SingleFileStorage.
 *
 * \param segmentSize
 *      The size in bytes of the segments this storage will deal with.
 * \param segmentFrames
 *      The number of segments this storage can store simultaneously.
 * \param filePath
 *      A filesystem path to the device or file where segments will be stored.
 * \param openFlags
 *      Extra flags for use while opening filePath (default to 0, O_DIRECT may
 *      be used to disable the OS buffer cache.
 */
SingleFileStorage::SingleFileStorage(uint32_t segmentSize,
                                     uint32_t segmentFrames,
                                     const char* filePath,
                                     int openFlags)
    : BackupStorage(segmentSize, Type::DISK)
    , freeMap(segmentFrames)
    , fd(-1)
    , killMessage()
    , killMessageLen()
    , lastAllocatedFrame(FreeMap::npos)
    , segmentFrames(segmentFrames)
{
    const char* killMessageStr = "FREE";
    // Must write block size of larger when O_DIRECT.
    killMessageLen = 512;
    int r = posix_memalign(&killMessage, killMessageLen, killMessageLen);
    if (r != 0)
        throw std::bad_alloc();
    memset(killMessage, 0, killMessageLen);
    memcpy(killMessage, killMessageStr, strlen(killMessageStr));

    freeMap.set();

    fd = open(filePath,
              O_CREAT | O_RDWR | openFlags,
              0666);
    if (fd == -1)
        throw BackupStorageException(HERE,
              format("Failed to open backup storage file %s", filePath), errno);

    // If its a regular file reserve space, otherwise
    // assume its a device and we don't need to bother.
    struct stat st;
    r = stat(filePath, &st);
    if (r == -1)
        return;
    if (st.st_mode & S_IFREG)
        reserveSpace();
}

/// Close the file.
SingleFileStorage::~SingleFileStorage()
{
    int r = close(fd);
    if (r == -1)
        LOG(ERROR, "Couldn't close backup log");
    std::free(killMessage);
}

// See BackupStorage::allocate().
BackupStorage::Handle*
SingleFileStorage::allocate(uint64_t masterId,
                            uint64_t segmentId)
{
    FreeMap::size_type next = freeMap.find_next(lastAllocatedFrame);
    if (next == FreeMap::npos) {
        next = freeMap.find_first();
        if (next == FreeMap::npos)
            throw BackupStorageException(HERE, "Out of free segment frames.");
    }
    lastAllocatedFrame = next;
    uint32_t targetSegmentFrame = static_cast<uint32_t>(next);
    LOG(DEBUG, "Writing <%lu,%lu> to frame %u", masterId, segmentId,
                                                targetSegmentFrame);
    freeMap[targetSegmentFrame] = 0;
    return new Handle(targetSegmentFrame);
}

/**
 * Same as BackupStorage::benchmark() except it resets the storage to reuse
 * the segment frames that may have been used during benchmarking.
 * This allows benchmark to be called without
 * wasting early segment frames on the disk which may be faster.
 */
pair<uint32_t, uint32_t>
SingleFileStorage::benchmark()
{
    auto r = BackupStorage::benchmark();
    lastAllocatedFrame = FreeMap::npos;
    return r;
}

// See BackupStorage::free().
void
SingleFileStorage::free(BackupStorage::Handle* handle)
{
    uint32_t segmentFrame =
        static_cast<const Handle*>(handle)->getSegmentFrame();

    off_t offset = lseek(fd,
                         offsetOfSegmentFrame(segmentFrame),
                         SEEK_SET);
    if (offset == -1)
        throw BackupStorageException(HERE,
                "Failed to seek to segment frame to free storage", errno);
    ssize_t r = write(fd, killMessage, killMessageLen);
    if (r != killMessageLen)
        throw BackupStorageException(HERE,
                "Couldn't overwrite stored segment header to free", errno);

    freeMap[segmentFrame] = 1;
    delete handle;
}

// See BackupStorage::getSegment().
// NOTE: This must remain thread-safe, so be careful about adding
// access to other resources.
void
SingleFileStorage::getSegment(const BackupStorage::Handle* handle,
                              char* segment) const
{
    uint32_t sourceSegmentFrame =
        static_cast<const Handle*>(handle)->getSegmentFrame();
    off_t offset = offsetOfSegmentFrame(sourceSegmentFrame);
    ssize_t r = pread(fd, segment, segmentSize, offset);
    if (r != static_cast<ssize_t>(segmentSize))
        throw BackupStorageException(HERE, errno);
}

// See BackupStorage::putSegment().
// NOTE: This must remain thread-safe, so be careful about adding
// access to other resources.
void
SingleFileStorage::putSegment(const BackupStorage::Handle* handle,
                              const char* segment) const
{
    uint32_t targetSegmentFrame =
        static_cast<const Handle*>(handle)->getSegmentFrame();
    off_t offset = offsetOfSegmentFrame(targetSegmentFrame);
    ssize_t r = pwrite(fd, segment, segmentSize, offset);
    if (r != static_cast<ssize_t>(segmentSize))
        throw BackupStorageException(HERE, errno);
}

// - private -

/**
 * Pure function.
 *
 * \param segmentFrame
 *      The segmentFrame to find the offset of in the file.
 * \return
 *      The offset into the file a particular segmentFrame starts at.
 */
uint64_t
SingleFileStorage::offsetOfSegmentFrame(uint32_t segmentFrame) const
{
    return segmentFrame * segmentSize;
}

/**
 * Fix the size of the logfile to ensure that the OS doesn't tell us
 * the filesystem is out of space later.
 *
 * \throw BackupStorageException
 *      If space for segmentFrames segments of segmentSize cannot be reserved.
 */
void
SingleFileStorage::reserveSpace()
{
    uint64_t logSpace = segmentSize;
    logSpace *= segmentFrames;

    LOG(DEBUG, "Reserving %lu bytes of log space", logSpace);
    int r = ftruncate(fd, logSpace);
    if (r == -1)
        throw BackupStorageException(HERE,
                "Couldn't reserve storage space for backup", errno);
}

// --- InMemoryStorage ---

// - public -

/**
 * Create an in-memory storage area.
 *
 * \param segmentSize
 *      The size of segments this storage will house.
 * \param segmentFrames
 *      The number of segments this storage can house.
 */
InMemoryStorage::InMemoryStorage(uint32_t segmentSize,
                                 uint32_t segmentFrames)
    : BackupStorage(segmentSize, Type::MEMORY)
    , pool(segmentSize)
    , segmentFrames(segmentFrames)
{
}

/// Free up all storage.
InMemoryStorage::~InMemoryStorage()
{
}

// See BackupStorage::allocate().
BackupStorage::Handle*
InMemoryStorage::allocate(uint64_t masterId,
                          uint64_t segmentId)
{
    if (!segmentFrames)
        throw BackupStorageException(HERE, "Out of free segment frames.");
    segmentFrames--;
    char* address = static_cast<char *>(pool.malloc());
    return new Handle(address);
}

// See BackupStorage::free().
void
InMemoryStorage::free(BackupStorage::Handle* handle)
{
    TEST_LOG("called");
    char* address =
        static_cast<const Handle*>(handle)->getAddress();
    memcpy(address, "FREE", 4);
    pool.free(address);
    delete handle;
}

// See BackupStorage::getSegment().
// NOTE: This must remain thread-safe, so be careful about adding
// access to other resources.
void
InMemoryStorage::getSegment(const BackupStorage::Handle* handle,
                            char* segment) const
{
    char* address = static_cast<const Handle*>(handle)->getAddress();
    memcpy(segment, address, segmentSize);
}

// See BackupStorage::putSegment().
// NOTE: This must remain thread-safe, so be careful about adding
// access to other resources.
void
InMemoryStorage::putSegment(const BackupStorage::Handle* handle,
                            const char* segment) const
{
    char* address = static_cast<const Handle*>(handle)->getAddress();
    memcpy(address, segment, segmentSize);
}

} // namespace RAMCloud
<commit_msg>Fix backup storage benchmark<commit_after>/* Copyright (c) 2010 Stanford University
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include "Common.h"
#include "BackupStorage.h"

namespace RAMCloud {

// --- BackupStorage ---

/**
 * Report the read and write speed of this storage in MB/s.
 *
 * \return
 *      A pair of storage read and write speeds in MB/s.
 */
pair<uint32_t, uint32_t>
BackupStorage::benchmark()
{
    const uint32_t count = 16;
    uint32_t readSpeeds[count];
    uint32_t writeSpeeds[count];
    BackupStorage::Handle* handles[count];

    void* p = xmemalign(Segment::SEGMENT_SIZE, Segment::SEGMENT_SIZE);
    char* segment = static_cast<char*>(p);

    try {
        for (uint32_t i = 0; i < count; ++i)
            handles[i] = NULL;

        for (uint32_t i = 0; i < count; ++i)
            handles[i] = allocate(0, i + 1);

        for (uint32_t i = 0; i < count; ++i) {
            CycleCounter<> counter;
            putSegment(handles[i], segment);
            uint64_t ns = cyclesToNanoseconds(counter.stop());
            writeSpeeds[i] = downCast<uint32_t>(
                                Segment::SEGMENT_SIZE * 1000UL * 1000 * 1000 /
                                (1 << 20) / ns);
        }
        for (uint32_t i = 0; i < count; ++i) {
            CycleCounter<> counter;
            getSegment(handles[i], segment);
            uint64_t ns = cyclesToNanoseconds(counter.stop());
            readSpeeds[i] = downCast<uint32_t>(
                                Segment::SEGMENT_SIZE * 1000UL * 1000 * 1000 /
                                (1 << 20) / ns);
        }
    } catch (...) {
        std::free(segment);
        for (uint32_t i = 0; i < count; ++i)
            if (handles[i])
                free(handles[i]);
        throw;
    }

    std::free(segment);
    for (uint32_t i = 0; i < count; ++i)
        free(handles[i]);

    uint32_t minRead = *std::min_element(readSpeeds,
                                         readSpeeds + count);
    uint32_t minWrite = *std::min_element(writeSpeeds,
                                          writeSpeeds + count);
    uint32_t avgRead = ({
        uint32_t sum = 0;
        foreach (uint32_t speed, readSpeeds)
            sum += speed;
        sum / count;
    });
    uint32_t avgWrite = ({
        uint32_t sum = 0;
        foreach (uint32_t speed, writeSpeeds)
            sum += speed;
        sum / count;
    });

    LOG(NOTICE, "Backup storage speeds (min): %u MB/s read, %u MB/s write",
        minRead, minWrite);
    LOG(NOTICE, "Backup storage speeds (avg): %u MB/s read, %u MB/s write",
        avgRead, avgWrite);

    return {avgRead, avgWrite};
}

// --- BackupStorage::Handle ---

int32_t BackupStorage::Handle::allocatedHandlesCount = 0;

// --- SingleFileStorage ---

// - public -

/**
 * Create a SingleFileStorage.
 *
 * \param segmentSize
 *      The size in bytes of the segments this storage will deal with.
 * \param segmentFrames
 *      The number of segments this storage can store simultaneously.
 * \param filePath
 *      A filesystem path to the device or file where segments will be stored.
 * \param openFlags
 *      Extra flags for use while opening filePath (default to 0, O_DIRECT may
 *      be used to disable the OS buffer cache.
 */
SingleFileStorage::SingleFileStorage(uint32_t segmentSize,
                                     uint32_t segmentFrames,
                                     const char* filePath,
                                     int openFlags)
    : BackupStorage(segmentSize, Type::DISK)
    , freeMap(segmentFrames)
    , fd(-1)
    , killMessage()
    , killMessageLen()
    , lastAllocatedFrame(FreeMap::npos)
    , segmentFrames(segmentFrames)
{
    const char* killMessageStr = "FREE";
    // Must write block size of larger when O_DIRECT.
    killMessageLen = 512;
    int r = posix_memalign(&killMessage, killMessageLen, killMessageLen);
    if (r != 0)
        throw std::bad_alloc();
    memset(killMessage, 0, killMessageLen);
    memcpy(killMessage, killMessageStr, strlen(killMessageStr));

    freeMap.set();

    fd = open(filePath,
              O_CREAT | O_RDWR | openFlags,
              0666);
    if (fd == -1)
        throw BackupStorageException(HERE,
              format("Failed to open backup storage file %s", filePath), errno);

    // If its a regular file reserve space, otherwise
    // assume its a device and we don't need to bother.
    struct stat st;
    r = stat(filePath, &st);
    if (r == -1)
        return;
    if (st.st_mode & S_IFREG)
        reserveSpace();
}

/// Close the file.
SingleFileStorage::~SingleFileStorage()
{
    int r = close(fd);
    if (r == -1)
        LOG(ERROR, "Couldn't close backup log");
    std::free(killMessage);
}

// See BackupStorage::allocate().
BackupStorage::Handle*
SingleFileStorage::allocate(uint64_t masterId,
                            uint64_t segmentId)
{
    FreeMap::size_type next = freeMap.find_next(lastAllocatedFrame);
    if (next == FreeMap::npos) {
        next = freeMap.find_first();
        if (next == FreeMap::npos)
            throw BackupStorageException(HERE, "Out of free segment frames.");
    }
    lastAllocatedFrame = next;
    uint32_t targetSegmentFrame = static_cast<uint32_t>(next);
    LOG(DEBUG, "Writing <%lu,%lu> to frame %u", masterId, segmentId,
                                                targetSegmentFrame);
    freeMap[targetSegmentFrame] = 0;
    return new Handle(targetSegmentFrame);
}

/**
 * Same as BackupStorage::benchmark() except it resets the storage to reuse
 * the segment frames that may have been used during benchmarking.
 * This allows benchmark to be called without
 * wasting early segment frames on the disk which may be faster.
 */
pair<uint32_t, uint32_t>
SingleFileStorage::benchmark()
{
    auto r = BackupStorage::benchmark();
    lastAllocatedFrame = FreeMap::npos;
    return r;
}

// See BackupStorage::free().
void
SingleFileStorage::free(BackupStorage::Handle* handle)
{
    uint32_t segmentFrame =
        static_cast<const Handle*>(handle)->getSegmentFrame();

    off_t offset = lseek(fd,
                         offsetOfSegmentFrame(segmentFrame),
                         SEEK_SET);
    if (offset == -1)
        throw BackupStorageException(HERE,
                "Failed to seek to segment frame to free storage", errno);
    ssize_t r = write(fd, killMessage, killMessageLen);
    if (r != killMessageLen)
        throw BackupStorageException(HERE,
                "Couldn't overwrite stored segment header to free", errno);

    freeMap[segmentFrame] = 1;
    delete handle;
}

// See BackupStorage::getSegment().
// NOTE: This must remain thread-safe, so be careful about adding
// access to other resources.
void
SingleFileStorage::getSegment(const BackupStorage::Handle* handle,
                              char* segment) const
{
    uint32_t sourceSegmentFrame =
        static_cast<const Handle*>(handle)->getSegmentFrame();
    off_t offset = offsetOfSegmentFrame(sourceSegmentFrame);
    ssize_t r = pread(fd, segment, segmentSize, offset);
    if (r != static_cast<ssize_t>(segmentSize))
        throw BackupStorageException(HERE, errno);
}

// See BackupStorage::putSegment().
// NOTE: This must remain thread-safe, so be careful about adding
// access to other resources.
void
SingleFileStorage::putSegment(const BackupStorage::Handle* handle,
                              const char* segment) const
{
    uint32_t targetSegmentFrame =
        static_cast<const Handle*>(handle)->getSegmentFrame();
    off_t offset = offsetOfSegmentFrame(targetSegmentFrame);
    ssize_t r = pwrite(fd, segment, segmentSize, offset);
    if (r != static_cast<ssize_t>(segmentSize))
        throw BackupStorageException(HERE, errno);
}

// - private -

/**
 * Pure function.
 *
 * \param segmentFrame
 *      The segmentFrame to find the offset of in the file.
 * \return
 *      The offset into the file a particular segmentFrame starts at.
 */
uint64_t
SingleFileStorage::offsetOfSegmentFrame(uint32_t segmentFrame) const
{
    return segmentFrame * segmentSize;
}

/**
 * Fix the size of the logfile to ensure that the OS doesn't tell us
 * the filesystem is out of space later.
 *
 * \throw BackupStorageException
 *      If space for segmentFrames segments of segmentSize cannot be reserved.
 */
void
SingleFileStorage::reserveSpace()
{
    uint64_t logSpace = segmentSize;
    logSpace *= segmentFrames;

    LOG(DEBUG, "Reserving %lu bytes of log space", logSpace);
    int r = ftruncate(fd, logSpace);
    if (r == -1)
        throw BackupStorageException(HERE,
                "Couldn't reserve storage space for backup", errno);
}

// --- InMemoryStorage ---

// - public -

/**
 * Create an in-memory storage area.
 *
 * \param segmentSize
 *      The size of segments this storage will house.
 * \param segmentFrames
 *      The number of segments this storage can house.
 */
InMemoryStorage::InMemoryStorage(uint32_t segmentSize,
                                 uint32_t segmentFrames)
    : BackupStorage(segmentSize, Type::MEMORY)
    , pool(segmentSize)
    , segmentFrames(segmentFrames)
{
}

/// Free up all storage.
InMemoryStorage::~InMemoryStorage()
{
}

// See BackupStorage::allocate().
BackupStorage::Handle*
InMemoryStorage::allocate(uint64_t masterId,
                          uint64_t segmentId)
{
    if (!segmentFrames)
        throw BackupStorageException(HERE, "Out of free segment frames.");
    segmentFrames--;
    char* address = static_cast<char *>(pool.malloc());
    return new Handle(address);
}

// See BackupStorage::free().
void
InMemoryStorage::free(BackupStorage::Handle* handle)
{
    TEST_LOG("called");
    char* address =
        static_cast<const Handle*>(handle)->getAddress();
    memcpy(address, "FREE", 4);
    pool.free(address);
    delete handle;
}

// See BackupStorage::getSegment().
// NOTE: This must remain thread-safe, so be careful about adding
// access to other resources.
void
InMemoryStorage::getSegment(const BackupStorage::Handle* handle,
                            char* segment) const
{
    char* address = static_cast<const Handle*>(handle)->getAddress();
    memcpy(segment, address, segmentSize);
}

// See BackupStorage::putSegment().
// NOTE: This must remain thread-safe, so be careful about adding
// access to other resources.
void
InMemoryStorage::putSegment(const BackupStorage::Handle* handle,
                            const char* segment) const
{
    char* address = static_cast<const Handle*>(handle)->getAddress();
    memcpy(address, segment, segmentSize);
}

} // namespace RAMCloud
<|endoftext|>


<commit_before>//                       MFEM Example 14 - Parallel Version
//
// Compile with: make ex14p
//
// Sample runs:  mpirun -np 4 ex14p -m ../data/inline-quad.mesh -o 0
//               mpirun -np 4 ex14p -m ../data/star.mesh -o 2
//               mpirun -np 4 ex14p -m ../data/star-mixed.mesh -o 2
//               mpirun -np 4 ex14p -m ../data/star-mixed.mesh -o 2 -k 0 -e 1
//               mpirun -np 4 ex14p -m ../data/escher.mesh -s 1
//               mpirun -np 4 ex14p -m ../data/fichera.mesh -s 1 -k 1
//               mpirun -np 4 ex14p -m ../data/fichera-mixed.mesh -s 1 -k 1
//               mpirun -np 4 ex14p -m ../data/square-disc-p2.vtk -o 2
//               mpirun -np 4 ex14p -m ../data/square-disc-p3.mesh -o 3
//               mpirun -np 4 ex14p -m ../data/square-disc-nurbs.mesh -o 1
//               mpirun -np 4 ex14p -m ../data/disc-nurbs.mesh -rs 4 -o 2 -s 1 -k 0
//               mpirun -np 4 ex14p -m ../data/pipe-nurbs.mesh -o 1
//               mpirun -np 4 ex14p -m ../data/inline-segment.mesh -rs 5
//               mpirun -np 4 ex14p -m ../data/amr-quad.mesh -rs 3
//               mpirun -np 4 ex14p -m ../data/amr-hex.mesh
//
// Description:  This example code demonstrates the use of MFEM to define a
//               discontinuous Galerkin (DG) finite element discretization of
//               the Laplace problem -Delta u = 1 with homogeneous Dirichlet
//               boundary conditions. Finite element spaces of any order,
//               including zero on regular grids, are supported. The example
//               highlights the use of discontinuous spaces and DG-specific face
//               integrators.
//
//               We recommend viewing examples 1 and 9 before viewing this
//               example.

#include "mfem.hpp"
#include <fstream>
#include <iostream>

using namespace std;
using namespace mfem;

class CustomSolverMonitor : public IterativeSolverMonitor
{
public:
   CustomSolverMonitor(const ParMesh *m,
                       ParGridFunction *f) :
      pmesh(m),
      pgf(f) {}

   void MonitorSolution(int i, double norm, const Vector &x, bool final)
   {
      char vishost[] = "localhost";
      int  visport   = 19916;
      int  num_procs, myid;

      MPI_Comm_size(pmesh->GetComm(),&num_procs);
      MPI_Comm_rank(pmesh->GetComm(),&myid);

      pgf->SetFromTrueDofs(x);

      socketstream sol_sock(vishost, visport);
      sol_sock << "parallel " << num_procs << " " << myid << "\n";
      sol_sock.precision(8);
      sol_sock << "solution\n" << *pmesh << *pgf
               << "window_title 'Iteration no " << i << "'"
               << "keys rRjlc\n" << flush;
   }

private:
   const ParMesh *pmesh;
   ParGridFunction *pgf;
};

int main(int argc, char *argv[])
{
   // 1. Initialize MPI.
   int num_procs, myid;
   MPI_Init(&argc, &argv);
   MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
   MPI_Comm_rank(MPI_COMM_WORLD, &myid);

   // 2. Parse command-line options.
   const char *mesh_file = "../data/star.mesh";
   int ser_ref_levels = -1;
   int par_ref_levels = 2;
   int order = 1;
   double sigma = -1.0;
   double kappa = -1.0;
   double eta = 0.0;
   bool visualization = 1;

   OptionsParser args(argc, argv);
   args.AddOption(&mesh_file, "-m", "--mesh",
                  "Mesh file to use.");
   args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
                  "Number of times to refine the mesh uniformly in serial,"
                  " -1 for auto.");
   args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
                  "Number of times to refine the mesh uniformly in parallel.");
   args.AddOption(&order, "-o", "--order",
                  "Finite element order (polynomial degree) >= 0.");
   args.AddOption(&sigma, "-s", "--sigma",
                  "One of the three DG penalty parameters, typically +1/-1."
                  " See the documentation of class DGDiffusionIntegrator.");
   args.AddOption(&kappa, "-k", "--kappa",
                  "One of the three DG penalty parameters, should be positive."
                  " Negative values are replaced with (order+1)^2.");
   args.AddOption(&eta, "-e", "--eta", "BR2 penalty parameter.");
   args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
                  "--no-visualization",
                  "Enable or disable GLVis visualization.");
   args.Parse();
   if (!args.Good())
   {
      if (myid == 0)
      {
         args.PrintUsage(cout);
      }
      MPI_Finalize();
      return 1;
   }
   if (kappa < 0)
   {
      kappa = (order+1)*(order+1);
   }
   if (myid == 0)
   {
      args.PrintOptions(cout);
   }

   // 3. Read the (serial) mesh from the given mesh file on all processors. We
   //    can handle triangular, quadrilateral, tetrahedral and hexahedral meshes
   //    with the same code. NURBS meshes are projected to second order meshes.
   Mesh *mesh = new Mesh(mesh_file, 1, 1);
   int dim = mesh->Dimension();

   // 4. Refine the serial mesh on all processors to increase the resolution. In
   //    this example we do 'ser_ref_levels' of uniform refinement. By default,
   //    or if ser_ref_levels < 0, we choose it to be the largest number that
   //    gives a final mesh with no more than 50,000 elements.
   {
      if (ser_ref_levels < 0)
      {
         ser_ref_levels = (int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
      }
      for (int l = 0; l < ser_ref_levels; l++)
      {
         mesh->UniformRefinement();
      }
   }
   if (mesh->NURBSext)
   {
      mesh->SetCurvature(max(order, 1));
   }

   // 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
   //    this mesh further in parallel to increase the resolution. Once the
   //    parallel mesh is defined, the serial mesh can be deleted.
   ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
   delete mesh;
   {
      for (int l = 0; l < par_ref_levels; l++)
      {
         pmesh->UniformRefinement();
      }
   }

   // 6. Define a parallel finite element space on the parallel mesh. Here we
   //    use discontinuous finite elements of the specified order >= 0.
   FiniteElementCollection *fec = new DG_FECollection(order, dim);
   ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
   HYPRE_BigInt size = fespace->GlobalTrueVSize();
   if (myid == 0)
   {
      cout << "Number of unknowns: " << size << endl;
   }

   // 7. Set up the parallel linear form b(.) which corresponds to the
   //    right-hand side of the FEM linear system.
   ParLinearForm *b = new ParLinearForm(fespace);
   ConstantCoefficient one(1.0);
   ConstantCoefficient zero(0.0);
   b->AddDomainIntegrator(new DomainLFIntegrator(one));
   b->AddBdrFaceIntegrator(
      new DGDirichletLFIntegrator(zero, one, sigma, kappa));
   b->Assemble();

   // 8. Define the solution vector x as a parallel finite element grid function
   //    corresponding to fespace. Initialize x with initial guess of zero.
   ParGridFunction x(fespace);
   x = 0.0;

   // 9. Set up the bilinear form a(.,.) on the finite element space
   //    corresponding to the Laplacian operator -Delta, by adding the Diffusion
   //    domain integrator and the interior and boundary DG face integrators.
   //    Note that boundary conditions are imposed weakly in the form, so there
   //    is no need for dof elimination. After serial and parallel assembly we
   //    extract the corresponding parallel matrix A.
   ParBilinearForm *a = new ParBilinearForm(fespace);
   a->AddDomainIntegrator(new DiffusionIntegrator(one));
   a->AddInteriorFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
   a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
   if (eta > 0)
   {
      a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
      a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
   }
   a->Assemble();
   a->Finalize();

   // 10. Define the parallel (hypre) matrix and vectors representing a(.,.),
   //     b(.) and the finite element approximation.
   HypreParMatrix *A = a->ParallelAssemble();
   HypreParVector *B = b->ParallelAssemble();
   HypreParVector *X = x.ParallelProject();

   delete a;
   delete b;

   // 11. Depending on the symmetry of A, define and apply a parallel PCG or
   //     GMRES solver for AX=B using the BoomerAMG preconditioner from hypre.
   HypreSolver *amg = new HypreBoomerAMG(*A);
   if (sigma == -1.0)
   {
      HyprePCG pcg(*A);
      pcg.SetTol(1e-12);
      pcg.SetMaxIter(200);
      pcg.SetPrintLevel(2);
      pcg.SetPreconditioner(*amg);
      pcg.Mult(*B, *X);
   }
   else
   {
      CustomSolverMonitor monitor(pmesh, &x);
      GMRESSolver gmres(MPI_COMM_WORLD);
      gmres.SetAbsTol(0.0);
      gmres.SetRelTol(1e-12);
      gmres.SetMaxIter(200);
      gmres.SetKDim(10);
      gmres.SetPrintLevel(1);
      gmres.SetOperator(*A);
      gmres.SetPreconditioner(*amg);
      gmres.SetMonitor(monitor);
      gmres.Mult(*B, *X);
   }
   delete amg;

   // 12. Extract the parallel grid function corresponding to the finite element
   //     approximation X. This is the local solution on each processor.
   x = *X;

   // 13. Save the refined mesh and the solution in parallel. This output can
   //     be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
   {
      ostringstream mesh_name, sol_name;
      mesh_name << "mesh." << setfill('0') << setw(6) << myid;
      sol_name << "sol." << setfill('0') << setw(6) << myid;

      ofstream mesh_ofs(mesh_name.str().c_str());
      mesh_ofs.precision(8);
      pmesh->Print(mesh_ofs);

      ofstream sol_ofs(sol_name.str().c_str());
      sol_ofs.precision(8);
      x.Save(sol_ofs);
   }

   // 14. Send the solution by socket to a GLVis server.
   if (visualization)
   {
      char vishost[] = "localhost";
      int  visport   = 19916;
      socketstream sol_sock(vishost, visport);
      sol_sock << "parallel " << num_procs << " " << myid << "\n";
      sol_sock.precision(8);
      sol_sock << "solution\n" << *pmesh << x << flush;
   }

   // 15. Free the used memory.
   delete X;
   delete B;
   delete A;

   delete fespace;
   delete fec;
   delete pmesh;

   MPI_Finalize();

   return 0;
}
<commit_msg>Use smaller problem for ex14 sample run<commit_after>//                       MFEM Example 14 - Parallel Version
//
// Compile with: make ex14p
//
// Sample runs:  mpirun -np 4 ex14p -m ../data/inline-quad.mesh -o 0
//               mpirun -np 4 ex14p -m ../data/star.mesh -o 2
//               mpirun -np 4 ex14p -m ../data/star-mixed.mesh -o 2
//               mpirun -np 4 ex14p -m ../data/star-mixed.mesh -rs 0 -rp 1 -o 2 -k 0 -e 1
//               mpirun -np 4 ex14p -m ../data/escher.mesh -s 1
//               mpirun -np 4 ex14p -m ../data/fichera.mesh -s 1 -k 1
//               mpirun -np 4 ex14p -m ../data/fichera-mixed.mesh -s 1 -k 1
//               mpirun -np 4 ex14p -m ../data/square-disc-p2.vtk -o 2
//               mpirun -np 4 ex14p -m ../data/square-disc-p3.mesh -o 3
//               mpirun -np 4 ex14p -m ../data/square-disc-nurbs.mesh -o 1
//               mpirun -np 4 ex14p -m ../data/disc-nurbs.mesh -rs 4 -o 2 -s 1 -k 0
//               mpirun -np 4 ex14p -m ../data/pipe-nurbs.mesh -o 1
//               mpirun -np 4 ex14p -m ../data/inline-segment.mesh -rs 5
//               mpirun -np 4 ex14p -m ../data/amr-quad.mesh -rs 3
//               mpirun -np 4 ex14p -m ../data/amr-hex.mesh
//
// Description:  This example code demonstrates the use of MFEM to define a
//               discontinuous Galerkin (DG) finite element discretization of
//               the Laplace problem -Delta u = 1 with homogeneous Dirichlet
//               boundary conditions. Finite element spaces of any order,
//               including zero on regular grids, are supported. The example
//               highlights the use of discontinuous spaces and DG-specific face
//               integrators.
//
//               We recommend viewing examples 1 and 9 before viewing this
//               example.

#include "mfem.hpp"
#include <fstream>
#include <iostream>

using namespace std;
using namespace mfem;

class CustomSolverMonitor : public IterativeSolverMonitor
{
public:
   CustomSolverMonitor(const ParMesh *m,
                       ParGridFunction *f) :
      pmesh(m),
      pgf(f) {}

   void MonitorSolution(int i, double norm, const Vector &x, bool final)
   {
      char vishost[] = "localhost";
      int  visport   = 19916;
      int  num_procs, myid;

      MPI_Comm_size(pmesh->GetComm(),&num_procs);
      MPI_Comm_rank(pmesh->GetComm(),&myid);

      pgf->SetFromTrueDofs(x);

      socketstream sol_sock(vishost, visport);
      sol_sock << "parallel " << num_procs << " " << myid << "\n";
      sol_sock.precision(8);
      sol_sock << "solution\n" << *pmesh << *pgf
               << "window_title 'Iteration no " << i << "'"
               << "keys rRjlc\n" << flush;
   }

private:
   const ParMesh *pmesh;
   ParGridFunction *pgf;
};

int main(int argc, char *argv[])
{
   // 1. Initialize MPI.
   int num_procs, myid;
   MPI_Init(&argc, &argv);
   MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
   MPI_Comm_rank(MPI_COMM_WORLD, &myid);

   // 2. Parse command-line options.
   const char *mesh_file = "../data/star.mesh";
   int ser_ref_levels = -1;
   int par_ref_levels = 2;
   int order = 1;
   double sigma = -1.0;
   double kappa = -1.0;
   double eta = 0.0;
   bool visualization = 1;

   OptionsParser args(argc, argv);
   args.AddOption(&mesh_file, "-m", "--mesh",
                  "Mesh file to use.");
   args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
                  "Number of times to refine the mesh uniformly in serial,"
                  " -1 for auto.");
   args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
                  "Number of times to refine the mesh uniformly in parallel.");
   args.AddOption(&order, "-o", "--order",
                  "Finite element order (polynomial degree) >= 0.");
   args.AddOption(&sigma, "-s", "--sigma",
                  "One of the three DG penalty parameters, typically +1/-1."
                  " See the documentation of class DGDiffusionIntegrator.");
   args.AddOption(&kappa, "-k", "--kappa",
                  "One of the three DG penalty parameters, should be positive."
                  " Negative values are replaced with (order+1)^2.");
   args.AddOption(&eta, "-e", "--eta", "BR2 penalty parameter.");
   args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
                  "--no-visualization",
                  "Enable or disable GLVis visualization.");
   args.Parse();
   if (!args.Good())
   {
      if (myid == 0)
      {
         args.PrintUsage(cout);
      }
      MPI_Finalize();
      return 1;
   }
   if (kappa < 0)
   {
      kappa = (order+1)*(order+1);
   }
   if (myid == 0)
   {
      args.PrintOptions(cout);
   }

   // 3. Read the (serial) mesh from the given mesh file on all processors. We
   //    can handle triangular, quadrilateral, tetrahedral and hexahedral meshes
   //    with the same code. NURBS meshes are projected to second order meshes.
   Mesh *mesh = new Mesh(mesh_file, 1, 1);
   int dim = mesh->Dimension();

   // 4. Refine the serial mesh on all processors to increase the resolution. In
   //    this example we do 'ser_ref_levels' of uniform refinement. By default,
   //    or if ser_ref_levels < 0, we choose it to be the largest number that
   //    gives a final mesh with no more than 50,000 elements.
   {
      if (ser_ref_levels < 0)
      {
         ser_ref_levels = (int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
      }
      for (int l = 0; l < ser_ref_levels; l++)
      {
         mesh->UniformRefinement();
      }
   }
   if (mesh->NURBSext)
   {
      mesh->SetCurvature(max(order, 1));
   }

   // 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
   //    this mesh further in parallel to increase the resolution. Once the
   //    parallel mesh is defined, the serial mesh can be deleted.
   ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
   delete mesh;
   {
      for (int l = 0; l < par_ref_levels; l++)
      {
         pmesh->UniformRefinement();
      }
   }

   // 6. Define a parallel finite element space on the parallel mesh. Here we
   //    use discontinuous finite elements of the specified order >= 0.
   FiniteElementCollection *fec = new DG_FECollection(order, dim);
   ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
   HYPRE_BigInt size = fespace->GlobalTrueVSize();
   if (myid == 0)
   {
      cout << "Number of unknowns: " << size << endl;
   }

   // 7. Set up the parallel linear form b(.) which corresponds to the
   //    right-hand side of the FEM linear system.
   ParLinearForm *b = new ParLinearForm(fespace);
   ConstantCoefficient one(1.0);
   ConstantCoefficient zero(0.0);
   b->AddDomainIntegrator(new DomainLFIntegrator(one));
   b->AddBdrFaceIntegrator(
      new DGDirichletLFIntegrator(zero, one, sigma, kappa));
   b->Assemble();

   // 8. Define the solution vector x as a parallel finite element grid function
   //    corresponding to fespace. Initialize x with initial guess of zero.
   ParGridFunction x(fespace);
   x = 0.0;

   // 9. Set up the bilinear form a(.,.) on the finite element space
   //    corresponding to the Laplacian operator -Delta, by adding the Diffusion
   //    domain integrator and the interior and boundary DG face integrators.
   //    Note that boundary conditions are imposed weakly in the form, so there
   //    is no need for dof elimination. After serial and parallel assembly we
   //    extract the corresponding parallel matrix A.
   ParBilinearForm *a = new ParBilinearForm(fespace);
   a->AddDomainIntegrator(new DiffusionIntegrator(one));
   a->AddInteriorFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
   a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
   if (eta > 0)
   {
      a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
      a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
   }
   a->Assemble();
   a->Finalize();

   // 10. Define the parallel (hypre) matrix and vectors representing a(.,.),
   //     b(.) and the finite element approximation.
   HypreParMatrix *A = a->ParallelAssemble();
   HypreParVector *B = b->ParallelAssemble();
   HypreParVector *X = x.ParallelProject();

   delete a;
   delete b;

   // 11. Depending on the symmetry of A, define and apply a parallel PCG or
   //     GMRES solver for AX=B using the BoomerAMG preconditioner from hypre.
   HypreSolver *amg = new HypreBoomerAMG(*A);
   if (sigma == -1.0)
   {
      HyprePCG pcg(*A);
      pcg.SetTol(1e-12);
      pcg.SetMaxIter(200);
      pcg.SetPrintLevel(2);
      pcg.SetPreconditioner(*amg);
      pcg.Mult(*B, *X);
   }
   else
   {
      CustomSolverMonitor monitor(pmesh, &x);
      GMRESSolver gmres(MPI_COMM_WORLD);
      gmres.SetAbsTol(0.0);
      gmres.SetRelTol(1e-12);
      gmres.SetMaxIter(200);
      gmres.SetKDim(10);
      gmres.SetPrintLevel(1);
      gmres.SetOperator(*A);
      gmres.SetPreconditioner(*amg);
      gmres.SetMonitor(monitor);
      gmres.Mult(*B, *X);
   }
   delete amg;

   // 12. Extract the parallel grid function corresponding to the finite element
   //     approximation X. This is the local solution on each processor.
   x = *X;

   // 13. Save the refined mesh and the solution in parallel. This output can
   //     be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
   {
      ostringstream mesh_name, sol_name;
      mesh_name << "mesh." << setfill('0') << setw(6) << myid;
      sol_name << "sol." << setfill('0') << setw(6) << myid;

      ofstream mesh_ofs(mesh_name.str().c_str());
      mesh_ofs.precision(8);
      pmesh->Print(mesh_ofs);

      ofstream sol_ofs(sol_name.str().c_str());
      sol_ofs.precision(8);
      x.Save(sol_ofs);
   }

   // 14. Send the solution by socket to a GLVis server.
   if (visualization)
   {
      char vishost[] = "localhost";
      int  visport   = 19916;
      socketstream sol_sock(vishost, visport);
      sol_sock << "parallel " << num_procs << " " << myid << "\n";
      sol_sock.precision(8);
      sol_sock << "solution\n" << *pmesh << x << flush;
   }

   // 15. Free the used memory.
   delete X;
   delete B;
   delete A;

   delete fespace;
   delete fec;
   delete pmesh;

   MPI_Finalize();

   return 0;
}
<|endoftext|>


<commit_before><commit_msg>Fixed compiler warning<commit_after><|endoftext|>


<commit_before>//===-- X86VZeroUpper.cpp - AVX vzeroupper instruction inserter -----------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the pass which inserts x86 AVX vzeroupper instructions
// before calls to SSE encoded functions. This avoids transition latency
// penalty when transferring control between AVX encoded instructions and old
// SSE encoding mode.
//
//===----------------------------------------------------------------------===//

#include "X86.h"
#include "X86InstrInfo.h"
#include "X86Subtarget.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetInstrInfo.h"
using namespace llvm;

#define DEBUG_TYPE "x86-vzeroupper"

STATISTIC(NumVZU, "Number of vzeroupper instructions inserted");

namespace {

  class VZeroUpperInserter : public MachineFunctionPass {
  public:

    VZeroUpperInserter() : MachineFunctionPass(ID) {}
    bool runOnMachineFunction(MachineFunction &MF) override;
    MachineFunctionProperties getRequiredProperties() const override {
      return MachineFunctionProperties().set(
          MachineFunctionProperties::Property::AllVRegsAllocated);
    }
    const char *getPassName() const override {return "X86 vzeroupper inserter";}

  private:

    void processBasicBlock(MachineBasicBlock &MBB);
    void insertVZeroUpper(MachineBasicBlock::iterator I,
                          MachineBasicBlock &MBB);
    void addDirtySuccessor(MachineBasicBlock &MBB);

    typedef enum { PASS_THROUGH, EXITS_CLEAN, EXITS_DIRTY } BlockExitState;
    static const char* getBlockExitStateName(BlockExitState ST);

    // Core algorithm state:
    // BlockState - Each block is either:
    //   - PASS_THROUGH: There are neither YMM dirtying instructions nor
    //                   vzeroupper instructions in this block.
    //   - EXITS_CLEAN: There is (or will be) a vzeroupper instruction in this
    //                  block that will ensure that YMM is clean on exit.
    //   - EXITS_DIRTY: An instruction in the block dirties YMM and no
    //                  subsequent vzeroupper in the block clears it.
    //
    // AddedToDirtySuccessors - This flag is raised when a block is added to the
    //                          DirtySuccessors list to ensure that it's not
    //                          added multiple times.
    //
    // FirstUnguardedCall - Records the location of the first unguarded call in
    //                      each basic block that may need to be guarded by a
    //                      vzeroupper. We won't know whether it actually needs
    //                      to be guarded until we discover a predecessor that
    //                      is DIRTY_OUT.
    struct BlockState {
      BlockState() : ExitState(PASS_THROUGH), AddedToDirtySuccessors(false) {}
      BlockExitState ExitState;
      bool AddedToDirtySuccessors;
      MachineBasicBlock::iterator FirstUnguardedCall;
    };
    typedef SmallVector<BlockState, 8> BlockStateMap;
    typedef SmallVector<MachineBasicBlock*, 8> DirtySuccessorsWorkList;

    BlockStateMap BlockStates;
    DirtySuccessorsWorkList DirtySuccessors;
    bool EverMadeChange;
    bool IsX86INTR;
    const TargetInstrInfo *TII;

    static char ID;
  };

  char VZeroUpperInserter::ID = 0;
}

FunctionPass *llvm::createX86IssueVZeroUpperPass() {
  return new VZeroUpperInserter();
}

const char* VZeroUpperInserter::getBlockExitStateName(BlockExitState ST) {
  switch (ST) {
    case PASS_THROUGH: return "Pass-through";
    case EXITS_DIRTY: return "Exits-dirty";
    case EXITS_CLEAN: return "Exits-clean";
  }
  llvm_unreachable("Invalid block exit state.");
}

static bool isYmmReg(unsigned Reg) {
  return (Reg >= X86::YMM0 && Reg <= X86::YMM15);
}

static bool checkFnHasLiveInYmm(MachineRegisterInfo &MRI) {
  for (MachineRegisterInfo::livein_iterator I = MRI.livein_begin(),
       E = MRI.livein_end(); I != E; ++I)
    if (isYmmReg(I->first))
      return true;

  return false;
}

static bool clobbersAllYmmRegs(const MachineOperand &MO) {
  for (unsigned reg = X86::YMM0; reg <= X86::YMM15; ++reg) {
    if (!MO.clobbersPhysReg(reg))
      return false;
  }
  return true;
}

static bool hasYmmReg(MachineInstr *MI) {
  for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) {
    const MachineOperand &MO = MI->getOperand(i);
    if (MI->isCall() && MO.isRegMask() && !clobbersAllYmmRegs(MO))
      return true;
    if (!MO.isReg())
      continue;
    if (MO.isDebug())
      continue;
    if (isYmmReg(MO.getReg()))
      return true;
  }
  return false;
}

/// clobbersAnyYmmReg() - Check if any YMM register will be clobbered by this
/// instruction.
static bool callClobbersAnyYmmReg(MachineInstr *MI) {
  assert(MI->isCall() && "Can only be called on call instructions.");
  for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) {
    const MachineOperand &MO = MI->getOperand(i);
    if (!MO.isRegMask())
      continue;
    for (unsigned reg = X86::YMM0; reg <= X86::YMM15; ++reg) {
      if (MO.clobbersPhysReg(reg))
        return true;
    }
  }
  return false;
}

// Insert a vzeroupper instruction before I.
void VZeroUpperInserter::insertVZeroUpper(MachineBasicBlock::iterator I,
                                              MachineBasicBlock &MBB) {
  DebugLoc dl = I->getDebugLoc();
  BuildMI(MBB, I, dl, TII->get(X86::VZEROUPPER));
  ++NumVZU;
  EverMadeChange = true;
}

// Add MBB to the DirtySuccessors list if it hasn't already been added.
void VZeroUpperInserter::addDirtySuccessor(MachineBasicBlock &MBB) {
  if (!BlockStates[MBB.getNumber()].AddedToDirtySuccessors) {
    DirtySuccessors.push_back(&MBB);
    BlockStates[MBB.getNumber()].AddedToDirtySuccessors = true;
  }
}

/// processBasicBlock - Loop over all of the instructions in the basic block,
/// inserting vzeroupper instructions before function calls.
void VZeroUpperInserter::processBasicBlock(MachineBasicBlock &MBB) {

  // Start by assuming that the block PASS_THROUGH, which implies no unguarded
  // calls.
  BlockExitState CurState = PASS_THROUGH;
  BlockStates[MBB.getNumber()].FirstUnguardedCall = MBB.end();

  for (MachineBasicBlock::iterator I = MBB.begin(); I != MBB.end(); ++I) {
    MachineInstr *MI = I;
    // No need for vzeroupper before iret in interrupt handler function,
    // epilogue will restore YMM registers if needed.
    bool IsReturnFromX86INTR = IsX86INTR && MI->isReturn();
    bool IsControlFlow = MI->isCall() || MI->isReturn();

    // Shortcut: don't need to check regular instructions in dirty state.
    if ((!IsControlFlow || IsReturnFromX86INTR) && CurState == EXITS_DIRTY)
      continue;

    if (hasYmmReg(MI)) {
      // We found a ymm-using instruction; this could be an AVX instruction,
      // or it could be control flow.
      CurState = EXITS_DIRTY;
      continue;
    }

    // Check for control-flow out of the current function (which might
    // indirectly execute SSE instructions).
    if (!IsControlFlow || IsReturnFromX86INTR)
      continue;

    // If the call won't clobber any YMM register, skip it as well. It usually
    // happens on helper function calls (such as '_chkstk', '_ftol2') where
    // standard calling convention is not used (RegMask is not used to mark
    // register clobbered and register usage (def/imp-def/use) is well-defined
    // and explicitly specified.
    if (MI->isCall() && !callClobbersAnyYmmReg(MI))
      continue;

    // The VZEROUPPER instruction resets the upper 128 bits of all Intel AVX
    // registers. This instruction has zero latency. In addition, the processor
    // changes back to Clean state, after which execution of Intel SSE
    // instructions or Intel AVX instructions has no transition penalty. Add
    // the VZEROUPPER instruction before any function call/return that might
    // execute SSE code.
    // FIXME: In some cases, we may want to move the VZEROUPPER into a
    // predecessor block.
    if (CurState == EXITS_DIRTY) {
      // After the inserted VZEROUPPER the state becomes clean again, but
      // other YMM may appear before other subsequent calls or even before
      // the end of the BB.
      insertVZeroUpper(I, MBB);
      CurState = EXITS_CLEAN;
    } else if (CurState == PASS_THROUGH) {
      // If this block is currently in pass-through state and we encounter a
      // call then whether we need a vzeroupper or not depends on whether this
      // block has successors that exit dirty. Record the location of the call,
      // and set the state to EXITS_CLEAN, but do not insert the vzeroupper yet.
      // It will be inserted later if necessary.
      BlockStates[MBB.getNumber()].FirstUnguardedCall = I;
      CurState = EXITS_CLEAN;
    }
  }

  DEBUG(dbgs() << "MBB #" << MBB.getNumber() << " exit state: "
               << getBlockExitStateName(CurState) << '\n');

  if (CurState == EXITS_DIRTY)
    for (MachineBasicBlock::succ_iterator SI = MBB.succ_begin(),
                                          SE = MBB.succ_end();
         SI != SE; ++SI)
      addDirtySuccessor(**SI);

  BlockStates[MBB.getNumber()].ExitState = CurState;
}

/// runOnMachineFunction - Loop over all of the basic blocks, inserting
/// vzeroupper instructions before function calls.
bool VZeroUpperInserter::runOnMachineFunction(MachineFunction &MF) {
  const X86Subtarget &ST = MF.getSubtarget<X86Subtarget>();
  if (!ST.hasAVX() || ST.hasAVX512() || ST.hasFastPartialYMMWrite())
    return false;
  TII = ST.getInstrInfo();
  MachineRegisterInfo &MRI = MF.getRegInfo();
  EverMadeChange = false;
  IsX86INTR = MF.getFunction()->getCallingConv() == CallingConv::X86_INTR;

  bool FnHasLiveInYmm = checkFnHasLiveInYmm(MRI);

  // Fast check: if the function doesn't use any ymm registers, we don't need
  // to insert any VZEROUPPER instructions.  This is constant-time, so it is
  // cheap in the common case of no ymm use.
  bool YMMUsed = FnHasLiveInYmm;
  if (!YMMUsed) {
    const TargetRegisterClass *RC = &X86::VR256RegClass;
    for (TargetRegisterClass::iterator i = RC->begin(), e = RC->end(); i != e;
         i++) {
      if (!MRI.reg_nodbg_empty(*i)) {
        YMMUsed = true;
        break;
      }
    }
  }
  if (!YMMUsed) {
    return false;
  }

  assert(BlockStates.empty() && DirtySuccessors.empty() &&
         "X86VZeroUpper state should be clear");
  BlockStates.resize(MF.getNumBlockIDs());

  // Process all blocks. This will compute block exit states, record the first
  // unguarded call in each block, and add successors of dirty blocks to the
  // DirtySuccessors list.
  for (MachineBasicBlock &MBB : MF)
    processBasicBlock(MBB);

  // If any YMM regs are live in to this function, add the entry block to the
  // DirtySuccessors list
  if (FnHasLiveInYmm)
    addDirtySuccessor(MF.front());

  // Re-visit all blocks that are successors of EXITS_DIRTY bsocks. Add
  // vzeroupper instructions to unguarded calls, and propagate EXITS_DIRTY
  // through PASS_THROUGH blocks.
  while (!DirtySuccessors.empty()) {
    MachineBasicBlock &MBB = *DirtySuccessors.back();
    DirtySuccessors.pop_back();
    BlockState &BBState = BlockStates[MBB.getNumber()];

    // MBB is a successor of a dirty block, so its first call needs to be
    // guarded.
    if (BBState.FirstUnguardedCall != MBB.end())
      insertVZeroUpper(BBState.FirstUnguardedCall, MBB);

    // If this successor was a pass-through block then it is now dirty, and its
    // successors need to be added to the worklist (if they haven't been
    // already).
    if (BBState.ExitState == PASS_THROUGH) {
      DEBUG(dbgs() << "MBB #" << MBB.getNumber()
                   << " was Pass-through, is now Dirty-out.\n");
      for (MachineBasicBlock::succ_iterator SI = MBB.succ_begin(),
                                            SE = MBB.succ_end();
           SI != SE; ++SI)
        addDirtySuccessor(**SI);
    }
  }

  BlockStates.clear();
  return EverMadeChange;
}
<commit_msg>fix documentation comments; NFC<commit_after>//===-- X86VZeroUpper.cpp - AVX vzeroupper instruction inserter -----------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the pass which inserts x86 AVX vzeroupper instructions
// before calls to SSE encoded functions. This avoids transition latency
// penalty when transferring control between AVX encoded instructions and old
// SSE encoding mode.
//
//===----------------------------------------------------------------------===//

#include "X86.h"
#include "X86InstrInfo.h"
#include "X86Subtarget.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetInstrInfo.h"
using namespace llvm;

#define DEBUG_TYPE "x86-vzeroupper"

STATISTIC(NumVZU, "Number of vzeroupper instructions inserted");

namespace {

  class VZeroUpperInserter : public MachineFunctionPass {
  public:

    VZeroUpperInserter() : MachineFunctionPass(ID) {}
    bool runOnMachineFunction(MachineFunction &MF) override;
    MachineFunctionProperties getRequiredProperties() const override {
      return MachineFunctionProperties().set(
          MachineFunctionProperties::Property::AllVRegsAllocated);
    }
    const char *getPassName() const override {return "X86 vzeroupper inserter";}

  private:

    void processBasicBlock(MachineBasicBlock &MBB);
    void insertVZeroUpper(MachineBasicBlock::iterator I,
                          MachineBasicBlock &MBB);
    void addDirtySuccessor(MachineBasicBlock &MBB);

    typedef enum { PASS_THROUGH, EXITS_CLEAN, EXITS_DIRTY } BlockExitState;
    static const char* getBlockExitStateName(BlockExitState ST);

    // Core algorithm state:
    // BlockState - Each block is either:
    //   - PASS_THROUGH: There are neither YMM dirtying instructions nor
    //                   vzeroupper instructions in this block.
    //   - EXITS_CLEAN: There is (or will be) a vzeroupper instruction in this
    //                  block that will ensure that YMM is clean on exit.
    //   - EXITS_DIRTY: An instruction in the block dirties YMM and no
    //                  subsequent vzeroupper in the block clears it.
    //
    // AddedToDirtySuccessors - This flag is raised when a block is added to the
    //                          DirtySuccessors list to ensure that it's not
    //                          added multiple times.
    //
    // FirstUnguardedCall - Records the location of the first unguarded call in
    //                      each basic block that may need to be guarded by a
    //                      vzeroupper. We won't know whether it actually needs
    //                      to be guarded until we discover a predecessor that
    //                      is DIRTY_OUT.
    struct BlockState {
      BlockState() : ExitState(PASS_THROUGH), AddedToDirtySuccessors(false) {}
      BlockExitState ExitState;
      bool AddedToDirtySuccessors;
      MachineBasicBlock::iterator FirstUnguardedCall;
    };
    typedef SmallVector<BlockState, 8> BlockStateMap;
    typedef SmallVector<MachineBasicBlock*, 8> DirtySuccessorsWorkList;

    BlockStateMap BlockStates;
    DirtySuccessorsWorkList DirtySuccessors;
    bool EverMadeChange;
    bool IsX86INTR;
    const TargetInstrInfo *TII;

    static char ID;
  };

  char VZeroUpperInserter::ID = 0;
}

FunctionPass *llvm::createX86IssueVZeroUpperPass() {
  return new VZeroUpperInserter();
}

const char* VZeroUpperInserter::getBlockExitStateName(BlockExitState ST) {
  switch (ST) {
    case PASS_THROUGH: return "Pass-through";
    case EXITS_DIRTY: return "Exits-dirty";
    case EXITS_CLEAN: return "Exits-clean";
  }
  llvm_unreachable("Invalid block exit state.");
}

static bool isYmmReg(unsigned Reg) {
  return (Reg >= X86::YMM0 && Reg <= X86::YMM15);
}

static bool checkFnHasLiveInYmm(MachineRegisterInfo &MRI) {
  for (MachineRegisterInfo::livein_iterator I = MRI.livein_begin(),
       E = MRI.livein_end(); I != E; ++I)
    if (isYmmReg(I->first))
      return true;

  return false;
}

static bool clobbersAllYmmRegs(const MachineOperand &MO) {
  for (unsigned reg = X86::YMM0; reg <= X86::YMM15; ++reg) {
    if (!MO.clobbersPhysReg(reg))
      return false;
  }
  return true;
}

static bool hasYmmReg(MachineInstr *MI) {
  for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) {
    const MachineOperand &MO = MI->getOperand(i);
    if (MI->isCall() && MO.isRegMask() && !clobbersAllYmmRegs(MO))
      return true;
    if (!MO.isReg())
      continue;
    if (MO.isDebug())
      continue;
    if (isYmmReg(MO.getReg()))
      return true;
  }
  return false;
}

/// Check if any YMM register will be clobbered by this instruction.
static bool callClobbersAnyYmmReg(MachineInstr *MI) {
  assert(MI->isCall() && "Can only be called on call instructions.");
  for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) {
    const MachineOperand &MO = MI->getOperand(i);
    if (!MO.isRegMask())
      continue;
    for (unsigned reg = X86::YMM0; reg <= X86::YMM15; ++reg) {
      if (MO.clobbersPhysReg(reg))
        return true;
    }
  }
  return false;
}

/// Insert a vzeroupper instruction before I.
void VZeroUpperInserter::insertVZeroUpper(MachineBasicBlock::iterator I,
                                          MachineBasicBlock &MBB) {
  DebugLoc dl = I->getDebugLoc();
  BuildMI(MBB, I, dl, TII->get(X86::VZEROUPPER));
  ++NumVZU;
  EverMadeChange = true;
}

/// Add MBB to the DirtySuccessors list if it hasn't already been added.
void VZeroUpperInserter::addDirtySuccessor(MachineBasicBlock &MBB) {
  if (!BlockStates[MBB.getNumber()].AddedToDirtySuccessors) {
    DirtySuccessors.push_back(&MBB);
    BlockStates[MBB.getNumber()].AddedToDirtySuccessors = true;
  }
}

/// Loop over all of the instructions in the basic block, inserting vzeroupper
/// instructions before function calls.
void VZeroUpperInserter::processBasicBlock(MachineBasicBlock &MBB) {

  // Start by assuming that the block PASS_THROUGH, which implies no unguarded
  // calls.
  BlockExitState CurState = PASS_THROUGH;
  BlockStates[MBB.getNumber()].FirstUnguardedCall = MBB.end();

  for (MachineBasicBlock::iterator I = MBB.begin(); I != MBB.end(); ++I) {
    MachineInstr *MI = I;
    // No need for vzeroupper before iret in interrupt handler function,
    // epilogue will restore YMM registers if needed.
    bool IsReturnFromX86INTR = IsX86INTR && MI->isReturn();
    bool IsControlFlow = MI->isCall() || MI->isReturn();

    // Shortcut: don't need to check regular instructions in dirty state.
    if ((!IsControlFlow || IsReturnFromX86INTR) && CurState == EXITS_DIRTY)
      continue;

    if (hasYmmReg(MI)) {
      // We found a ymm-using instruction; this could be an AVX instruction,
      // or it could be control flow.
      CurState = EXITS_DIRTY;
      continue;
    }

    // Check for control-flow out of the current function (which might
    // indirectly execute SSE instructions).
    if (!IsControlFlow || IsReturnFromX86INTR)
      continue;

    // If the call won't clobber any YMM register, skip it as well. It usually
    // happens on helper function calls (such as '_chkstk', '_ftol2') where
    // standard calling convention is not used (RegMask is not used to mark
    // register clobbered and register usage (def/imp-def/use) is well-defined
    // and explicitly specified.
    if (MI->isCall() && !callClobbersAnyYmmReg(MI))
      continue;

    // The VZEROUPPER instruction resets the upper 128 bits of all Intel AVX
    // registers. This instruction has zero latency. In addition, the processor
    // changes back to Clean state, after which execution of Intel SSE
    // instructions or Intel AVX instructions has no transition penalty. Add
    // the VZEROUPPER instruction before any function call/return that might
    // execute SSE code.
    // FIXME: In some cases, we may want to move the VZEROUPPER into a
    // predecessor block.
    if (CurState == EXITS_DIRTY) {
      // After the inserted VZEROUPPER the state becomes clean again, but
      // other YMM may appear before other subsequent calls or even before
      // the end of the BB.
      insertVZeroUpper(I, MBB);
      CurState = EXITS_CLEAN;
    } else if (CurState == PASS_THROUGH) {
      // If this block is currently in pass-through state and we encounter a
      // call then whether we need a vzeroupper or not depends on whether this
      // block has successors that exit dirty. Record the location of the call,
      // and set the state to EXITS_CLEAN, but do not insert the vzeroupper yet.
      // It will be inserted later if necessary.
      BlockStates[MBB.getNumber()].FirstUnguardedCall = I;
      CurState = EXITS_CLEAN;
    }
  }

  DEBUG(dbgs() << "MBB #" << MBB.getNumber() << " exit state: "
               << getBlockExitStateName(CurState) << '\n');

  if (CurState == EXITS_DIRTY)
    for (MachineBasicBlock::succ_iterator SI = MBB.succ_begin(),
                                          SE = MBB.succ_end();
         SI != SE; ++SI)
      addDirtySuccessor(**SI);

  BlockStates[MBB.getNumber()].ExitState = CurState;
}

/// Loop over all of the basic blocks, inserting vzeroupper instructions before
/// function calls.
bool VZeroUpperInserter::runOnMachineFunction(MachineFunction &MF) {
  const X86Subtarget &ST = MF.getSubtarget<X86Subtarget>();
  if (!ST.hasAVX() || ST.hasAVX512() || ST.hasFastPartialYMMWrite())
    return false;
  TII = ST.getInstrInfo();
  MachineRegisterInfo &MRI = MF.getRegInfo();
  EverMadeChange = false;
  IsX86INTR = MF.getFunction()->getCallingConv() == CallingConv::X86_INTR;

  bool FnHasLiveInYmm = checkFnHasLiveInYmm(MRI);

  // Fast check: if the function doesn't use any ymm registers, we don't need
  // to insert any VZEROUPPER instructions.  This is constant-time, so it is
  // cheap in the common case of no ymm use.
  bool YMMUsed = FnHasLiveInYmm;
  if (!YMMUsed) {
    const TargetRegisterClass *RC = &X86::VR256RegClass;
    for (TargetRegisterClass::iterator i = RC->begin(), e = RC->end(); i != e;
         i++) {
      if (!MRI.reg_nodbg_empty(*i)) {
        YMMUsed = true;
        break;
      }
    }
  }
  if (!YMMUsed) {
    return false;
  }

  assert(BlockStates.empty() && DirtySuccessors.empty() &&
         "X86VZeroUpper state should be clear");
  BlockStates.resize(MF.getNumBlockIDs());

  // Process all blocks. This will compute block exit states, record the first
  // unguarded call in each block, and add successors of dirty blocks to the
  // DirtySuccessors list.
  for (MachineBasicBlock &MBB : MF)
    processBasicBlock(MBB);

  // If any YMM regs are live in to this function, add the entry block to the
  // DirtySuccessors list
  if (FnHasLiveInYmm)
    addDirtySuccessor(MF.front());

  // Re-visit all blocks that are successors of EXITS_DIRTY bsocks. Add
  // vzeroupper instructions to unguarded calls, and propagate EXITS_DIRTY
  // through PASS_THROUGH blocks.
  while (!DirtySuccessors.empty()) {
    MachineBasicBlock &MBB = *DirtySuccessors.back();
    DirtySuccessors.pop_back();
    BlockState &BBState = BlockStates[MBB.getNumber()];

    // MBB is a successor of a dirty block, so its first call needs to be
    // guarded.
    if (BBState.FirstUnguardedCall != MBB.end())
      insertVZeroUpper(BBState.FirstUnguardedCall, MBB);

    // If this successor was a pass-through block then it is now dirty, and its
    // successors need to be added to the worklist (if they haven't been
    // already).
    if (BBState.ExitState == PASS_THROUGH) {
      DEBUG(dbgs() << "MBB #" << MBB.getNumber()
                   << " was Pass-through, is now Dirty-out.\n");
      for (MachineBasicBlock::succ_iterator SI = MBB.succ_begin(),
                                            SE = MBB.succ_end();
           SI != SE; ++SI)
        addDirtySuccessor(**SI);
    }
  }

  BlockStates.clear();
  return EverMadeChange;
}
<|endoftext|>


<commit_before>#include <libport/finally.hh>

#include <ast/nary.hh>

#include <object/lobby.hh>
#include <object/object.hh>
#include <object/tag.hh>

#include <runner/shell.hh>

#include <scheduler/scheduler.hh>

namespace runner
{
  Shell::Shell(const rLobby& lobby,
	       scheduler::Scheduler& scheduler,
	       const libport::Symbol& name)
    : Interpreter(lobby,
		  scheduler,
		  0,
		  name)
    , executing_(false)
  {
  }

  void
  Shell::work()
  {
    while (true)
    {
      // Wait until we have some work to do
      if (commands_.empty())
      {
	executing_ = false;

	// The first yield will remember our possible side effect. Subsequent
	// ones will not and will pretend that we didn't have any side effect,
	// as we couldn't have influenced the system any further.
	yield_until_things_changed();

	libport::Finally finally(boost::bind(&Job::side_effect_free_set,
					     this,
					     side_effect_free_get()));
	side_effect_free_set(true);
	while (commands_.empty())
	  yield_until_things_changed();

	executing_ = true;
      }

      ast::rConstNary nary = commands_.front();
      commands_.pop_front();
      operator()(nary.get());
    }
  }

  void
  Shell::append_command(const ast::rConstNary& command)
  {
    commands_.push_back(command);
    scheduler_get().signal_world_change();
  }

  bool
  Shell::pending_command_get() const
  {
    return executing_ || !commands_.empty();
  }

  void
  Shell::pending_commands_clear()
  {
    commands_.clear();
  }

} // namespace runner
<commit_msg>Do not let the user make the toplevel non-interruptible.<commit_after>#include <libport/finally.hh>

#include <ast/nary.hh>

#include <object/lobby.hh>
#include <object/object.hh>
#include <object/tag.hh>

#include <runner/shell.hh>

#include <scheduler/scheduler.hh>

namespace runner
{
  Shell::Shell(const rLobby& lobby,
	       scheduler::Scheduler& scheduler,
	       const libport::Symbol& name)
    : Interpreter(lobby,
		  scheduler,
		  0,
		  name)
    , executing_(false)
  {
  }

  void
  Shell::work()
  {
    while (true)
    {
      // Wait until we have some work to do
      if (commands_.empty())
      {
	executing_ = false;

	// The first yield will remember our possible side effect. Subsequent
	// ones will not and will pretend that we didn't have any side effect,
	// as we couldn't have influenced the system any further.
	yield_until_things_changed();

	libport::Finally finally(boost::bind(&Job::side_effect_free_set,
					     this,
					     side_effect_free_get()));
	side_effect_free_set(true);
	while (commands_.empty())
	  yield_until_things_changed();

	executing_ = true;
      }

      ast::rConstNary nary = commands_.front();
      commands_.pop_front();
      operator()(nary.get());
      if (non_interruptible_get())
      {
        send_message("error", "the toplevel can not be non-interruptible");
        non_interruptible_set(false);
      }
    }
  }

  void
  Shell::append_command(const ast::rConstNary& command)
  {
    commands_.push_back(command);
    scheduler_get().signal_world_change();
  }

  bool
  Shell::pending_command_get() const
  {
    return executing_ || !commands_.empty();
  }

  void
  Shell::pending_commands_clear()
  {
    commands_.clear();
  }

} // namespace runner
<|endoftext|>


<commit_before>// $Id$

//**************************************************************************
//* This file is property of and copyright by the ALICE HLT Project        *
//* ALICE Experiment at CERN, All rights reserved.                         *
//*                                                                        *
//* Primary Authors: Matthias Richter <Matthias.Richter@ift.uib.no>        *
//*                  for The ALICE HLT Project.                            *
//*                                                                        *
//* Permission to use, copy, modify and distribute this software and its   *
//* documentation strictly for non-commercial purposes is hereby granted   *
//* without fee, provided that the above copyright notice appears in all   *
//* copies and that both the copyright notice and this permission notice   *
//* appear in the supporting documentation. The authors make no claims     *
//* about the suitability of this software for any purpose. It is          *
//* provided "as is" without express or implied warranty.                  *
//**************************************************************************

/** @file   AliHLTQADataMakerRec.cxx
    @author Matthias Richter
    @date   2010-03-10
    @brief  Steering class for the HLT offline QA
*/
#include "AliHLTQADataMakerRec.h"
#include "AliHLTMisc.h"
#include "AliHLTModuleAgent.h"
#include "AliRecoParam.h"
#include <iostream>
#include "TString.h"
#include "TObjString.h"
#include "TObjArray.h"

using namespace std;

/** ROOT macro for the implementation of ROOT specific class methods */
ClassImp(AliHLTQADataMakerRec)

AliHLTQADataMakerRec::AliHLTQADataMakerRec()
  : AliHLTQADataMakerBase()
  , fPlugins()
  , fFlags(0)
{
  // see header file for class documentation
  // or
  // refer to README to build package
  // or
  // visit http://web.ift.uib.no/~kjeks/doc/alice-hlt

  LoadAgents();
}

AliHLTQADataMakerRec::~AliHLTQADataMakerRec()
{
  // see header file for class documentation
}

int AliHLTQADataMakerRec::LoadAgents()
{
  // iterate over available agents and query class names of plugins
  TString plugins;
  for (AliHLTModuleAgent* pAgent=AliHLTModuleAgent::GetFirstAgent();
       pAgent!=NULL;
       pAgent=AliHLTModuleAgent::GetNextAgent()) {
    const char* modulePlugins=pAgent->GetQAPlugins();
    if (!modulePlugins || !modulePlugins[0]) continue;
    if (!plugins.IsNull() && !plugins.EndsWith(" ")) plugins+=" ";
    plugins+=modulePlugins;
  }
  if (!plugins.IsNull()) return LoadPlugins(plugins);
  return 0;
}

int AliHLTQADataMakerRec::LoadPlugins(const char* plugins)
{
  // load plugins from list of blank separated class names
  int iResult=0;
  TString strPlugins=plugins;
  TObjArray* tokens=strPlugins.Tokenize(" ");
  if (tokens) {
    TIter next(tokens);
    TObject* obj=NULL;
    while ((obj=next())) {
      if (obj->IsA()!=TObjString::Class()) continue;
      AliHLTQADataMakerBase* plugin=AliHLTMisc::LoadInstance((AliHLTQADataMakerBase*)0,
							     dynamic_cast<TObjString*>(obj)->GetString().Data());
      if (!plugin) continue;
      AliInfo(Form("using HLT QA plugin %s", plugin->IsA()->GetName()));
      fPlugins.Add(plugin);
    }
    delete tokens;
  }
  return iResult;
}

void AliHLTQADataMakerRec::StartOfDetectorCycle()
{
  // see header file for class documentation

  // this function is called multiple times by the framework, actually for every QA task
  // however, here we don't have any argument for the task
  // this class is initialized right before StartOfDetectorCycle is called, and depending
  // on the availibility of thr histogram arrays one can tell where we are ;-)
  unsigned init=0;
  if (fDigitsQAList!=NULL && (fFlags&kDigitsListInit)==0) {
    init|=kDigitsListInit; // indicate that plugins should be initialized for that task
    fFlags|=kDigitsListInit; // indicate that it was initialized
  }
  if (fESDsQAList!=NULL && (fFlags&kESDsListInit)==0) {
    init|=kESDsListInit; // indicate that plugins should be initialized for that task
    fFlags|=kESDsListInit; // indicate that it was initialized
  }
  if (fRawsQAList!=NULL && (fFlags&kRawsListInit)==0) {
    init|=kRawsListInit; // indicate that plugins should be initialized for that task
    fFlags|=kRawsListInit; // indicate that it was initialized
  }
  if (fRecPointsQAList!=NULL && (fFlags&kRecPointsListInit)==0) {
    init|=kRecPointsListInit; // indicate that plugins should be initialized for that task
    fFlags|=kRecPointsListInit; // indicate that it was initialized
  }

  TIter next(&fPlugins);
  TObject* obj=NULL;
  while ((obj=next())) {
    AliHLTQADataMakerBase* plugin=dynamic_cast<AliHLTQADataMakerBase*>(obj);
    if (!plugin) continue;
    // transfer the properties set in AliQAManager::GetQADataMaker to the plugin
    plugin->SetName(GetName());
    plugin->SetUniqueID(GetUniqueID());
    if (init&kDigitsListInit) plugin->Init(AliQAv1::GetTaskIndex("Digits"), 0);
    if (init&kESDsListInit) plugin->Init(AliQAv1::GetTaskIndex("ESDs"), 0);
    if (init&kRawsListInit) plugin->Init(AliQAv1::GetTaskIndex("Raws"), 0);
    if (init&kRecPointsListInit) plugin->Init(AliQAv1::GetTaskIndex("RecPoints"), 0);
    plugin->StartOfDetectorCycle();
  }
}

void AliHLTQADataMakerRec::EndOfDetectorCycle(AliQAv1::TASKINDEX_t task, TObjArray** list)
{
  // see header file for class documentation
  TIter next(&fPlugins);
  TObject* obj=NULL;
  while ((obj=next())) {
    AliHLTQADataMakerBase* plugin=dynamic_cast<AliHLTQADataMakerBase*>(obj);
    if (!plugin) continue;
    plugin->SetEventSpecie(GetEventSpecie());

    TObjArray** pluginList=NULL;
    if (task==AliQAv1::kESDS) {
      pluginList=plugin->GetESDsQAList();
    } else if (task==AliQAv1::kRECPOINTS) {
      pluginList=plugin->GetRecPointsQAList();
    } else if (task==AliQAv1::kDIGITS) {
      pluginList=plugin->GetDigitsQAList();
    } else if (task==AliQAv1::kRAWS) {
      pluginList=plugin->GetRawsQAList();
    }

    if (pluginList) {
      plugin->EndOfDetectorCycle(task, pluginList);
      for (Int_t specie = 0 ; specie < AliRecoParam::kNSpecies ; specie++) {
	if (!pluginList[specie]) continue;
	TIter nextentry(pluginList[specie]);
	TObject* entry=NULL;
	while ((entry=nextentry())) {
	  AliInfo(Form("cloning histogram %s for specie %d", entry->GetName(), specie));
	  list[specie]->Add(entry->Clone());
	}
      }
    }
  }

}

void AliHLTQADataMakerRec::MakeRaws(AliRawReader * rawReader)
{
  // see header file for class documentation
  if (!rawReader) return;

  TIter next(&fPlugins);
  TObject* obj=NULL;
  while ((obj=next())) {
    AliHLTQADataMakerBase* plugin=dynamic_cast<AliHLTQADataMakerBase*>(obj);
    if (!plugin) continue;
    plugin->SetEventSpecie(GetEventSpecie());
    plugin->MakeRaws(rawReader);
  }
}

void AliHLTQADataMakerRec::MakeESDs(AliESDEvent * esd, AliESDEvent* hltesd)
{
  // HLT QA on ESDs
  TIter next(&fPlugins);
  TObject* obj=NULL;
  while ((obj=next())) {
    AliHLTQADataMakerBase* plugin=dynamic_cast<AliHLTQADataMakerBase*>(obj);
    if (!plugin) continue;
    plugin->SetEventSpecie(GetEventSpecie());
    plugin->MakeESDs(esd, hltesd);
  }
}
<commit_msg>bugfix http://savannah.cern.ch/bugs/?70332 set gDirectory temporarily to NULL in order to avoid histograms to be added to directory. this is just a local fix, the real fix needs to be applied on the level of QA framework.<commit_after>// $Id$

//**************************************************************************
//* This file is property of and copyright by the ALICE HLT Project        *
//* ALICE Experiment at CERN, All rights reserved.                         *
//*                                                                        *
//* Primary Authors: Matthias Richter <Matthias.Richter@ift.uib.no>        *
//*                  for The ALICE HLT Project.                            *
//*                                                                        *
//* Permission to use, copy, modify and distribute this software and its   *
//* documentation strictly for non-commercial purposes is hereby granted   *
//* without fee, provided that the above copyright notice appears in all   *
//* copies and that both the copyright notice and this permission notice   *
//* appear in the supporting documentation. The authors make no claims     *
//* about the suitability of this software for any purpose. It is          *
//* provided "as is" without express or implied warranty.                  *
//**************************************************************************

/** @file   AliHLTQADataMakerRec.cxx
    @author Matthias Richter
    @date   2010-03-10
    @brief  Steering class for the HLT offline QA
*/
#include "AliHLTQADataMakerRec.h"
#include "AliHLTMisc.h"
#include "AliHLTModuleAgent.h"
#include "AliRecoParam.h"
#include <iostream>
#include "TString.h"
#include "TObjString.h"
#include "TObjArray.h"
#include "TDirectory.h"

using namespace std;

/** ROOT macro for the implementation of ROOT specific class methods */
ClassImp(AliHLTQADataMakerRec)

AliHLTQADataMakerRec::AliHLTQADataMakerRec()
  : AliHLTQADataMakerBase()
  , fPlugins()
  , fFlags(0)
{
  // see header file for class documentation
  // or
  // refer to README to build package
  // or
  // visit http://web.ift.uib.no/~kjeks/doc/alice-hlt

  LoadAgents();
}

AliHLTQADataMakerRec::~AliHLTQADataMakerRec()
{
  // see header file for class documentation
}

int AliHLTQADataMakerRec::LoadAgents()
{
  // iterate over available agents and query class names of plugins
  TString plugins;
  for (AliHLTModuleAgent* pAgent=AliHLTModuleAgent::GetFirstAgent();
       pAgent!=NULL;
       pAgent=AliHLTModuleAgent::GetNextAgent()) {
    const char* modulePlugins=pAgent->GetQAPlugins();
    if (!modulePlugins || !modulePlugins[0]) continue;
    if (!plugins.IsNull() && !plugins.EndsWith(" ")) plugins+=" ";
    plugins+=modulePlugins;
  }
  if (!plugins.IsNull()) return LoadPlugins(plugins);
  return 0;
}

int AliHLTQADataMakerRec::LoadPlugins(const char* plugins)
{
  // load plugins from list of blank separated class names
  int iResult=0;
  TString strPlugins=plugins;
  TObjArray* tokens=strPlugins.Tokenize(" ");
  if (tokens) {
    TIter next(tokens);
    TObject* obj=NULL;
    while ((obj=next())) {
      if (obj->IsA()!=TObjString::Class()) continue;
      AliHLTQADataMakerBase* plugin=AliHLTMisc::LoadInstance((AliHLTQADataMakerBase*)0,
							     dynamic_cast<TObjString*>(obj)->GetString().Data());
      if (!plugin) continue;
      AliInfo(Form("using HLT QA plugin %s", plugin->IsA()->GetName()));
      fPlugins.Add(plugin);
    }
    delete tokens;
  }
  return iResult;
}

void AliHLTQADataMakerRec::StartOfDetectorCycle()
{
  // see header file for class documentation

  // this function is called multiple times by the framework, actually for every QA task
  // however, here we don't have any argument for the task
  // this class is initialized right before StartOfDetectorCycle is called, and depending
  // on the availibility of thr histogram arrays one can tell where we are ;-)
  unsigned init=0;
  if (fDigitsQAList!=NULL && (fFlags&kDigitsListInit)==0) {
    init|=kDigitsListInit; // indicate that plugins should be initialized for that task
    fFlags|=kDigitsListInit; // indicate that it was initialized
  }
  if (fESDsQAList!=NULL && (fFlags&kESDsListInit)==0) {
    init|=kESDsListInit; // indicate that plugins should be initialized for that task
    fFlags|=kESDsListInit; // indicate that it was initialized
  }
  if (fRawsQAList!=NULL && (fFlags&kRawsListInit)==0) {
    init|=kRawsListInit; // indicate that plugins should be initialized for that task
    fFlags|=kRawsListInit; // indicate that it was initialized
  }
  if (fRecPointsQAList!=NULL && (fFlags&kRecPointsListInit)==0) {
    init|=kRecPointsListInit; // indicate that plugins should be initialized for that task
    fFlags|=kRecPointsListInit; // indicate that it was initialized
  }

  TIter next(&fPlugins);
  TObject* obj=NULL;
  while ((obj=next())) {
    AliHLTQADataMakerBase* plugin=dynamic_cast<AliHLTQADataMakerBase*>(obj);
    if (!plugin) continue;
    // transfer the properties set in AliQAManager::GetQADataMaker to the plugin
    plugin->SetName(GetName());
    plugin->SetUniqueID(GetUniqueID());
    if (init&kDigitsListInit) plugin->Init(AliQAv1::GetTaskIndex("Digits"), 0);
    if (init&kESDsListInit) plugin->Init(AliQAv1::GetTaskIndex("ESDs"), 0);
    if (init&kRawsListInit) plugin->Init(AliQAv1::GetTaskIndex("Raws"), 0);
    if (init&kRecPointsListInit) plugin->Init(AliQAv1::GetTaskIndex("RecPoints"), 0);
    plugin->StartOfDetectorCycle();
  }
}

void AliHLTQADataMakerRec::EndOfDetectorCycle(AliQAv1::TASKINDEX_t task, TObjArray** list)
{
  // see header file for class documentation
  TIter next(&fPlugins);
  TObject* obj=NULL;
  TDirectory* dirBackup=gDirectory;
  gDirectory=NULL;
  while ((obj=next())) {
    AliHLTQADataMakerBase* plugin=dynamic_cast<AliHLTQADataMakerBase*>(obj);
    if (!plugin) continue;
    plugin->SetEventSpecie(GetEventSpecie());

    TObjArray** pluginList=NULL;
    if (task==AliQAv1::kESDS) {
      pluginList=plugin->GetESDsQAList();
    } else if (task==AliQAv1::kRECPOINTS) {
      pluginList=plugin->GetRecPointsQAList();
    } else if (task==AliQAv1::kDIGITS) {
      pluginList=plugin->GetDigitsQAList();
    } else if (task==AliQAv1::kRAWS) {
      pluginList=plugin->GetRawsQAList();
    }

    if (pluginList) {
      plugin->EndOfDetectorCycle(task, pluginList);
      for (Int_t specie = 0 ; specie < AliRecoParam::kNSpecies ; specie++) {
	if (!pluginList[specie]) continue;
	TIter nextentry(pluginList[specie]);
	TObject* entry=NULL;
	while ((entry=nextentry())) {
	  AliInfo(Form("cloning histogram %s for specie %d", entry->GetName(), specie));
	  list[specie]->Add(entry->Clone());
	}
      }
    }
  }
  gDirectory=dirBackup;
}

void AliHLTQADataMakerRec::MakeRaws(AliRawReader * rawReader)
{
  // see header file for class documentation
  if (!rawReader) return;

  TIter next(&fPlugins);
  TObject* obj=NULL;
  while ((obj=next())) {
    AliHLTQADataMakerBase* plugin=dynamic_cast<AliHLTQADataMakerBase*>(obj);
    if (!plugin) continue;
    plugin->SetEventSpecie(GetEventSpecie());
    plugin->MakeRaws(rawReader);
  }
}

void AliHLTQADataMakerRec::MakeESDs(AliESDEvent * esd, AliESDEvent* hltesd)
{
  // HLT QA on ESDs
  TIter next(&fPlugins);
  TObject* obj=NULL;
  while ((obj=next())) {
    AliHLTQADataMakerBase* plugin=dynamic_cast<AliHLTQADataMakerBase*>(obj);
    if (!plugin) continue;
    plugin->SetEventSpecie(GetEventSpecie());
    plugin->MakeESDs(esd, hltesd);
  }
}
<|endoftext|>