<commit_before>#ifndef __RANK_FILTER__
#define __RANK_FILTER__

#include <deque>
#include <cassert>
#include <functional>
#include <iostream>
#include <iterator>

#include <boost/array.hpp>
#include <boost/container/set.hpp>
#include <boost/container/node_allocator.hpp>
#include <boost/math/special_functions/round.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>

namespace rank_filter
{

template<class I1,
         class I2>
inline void lineRankOrderFilter1D(const I1& src_begin, const I1& src_end,
        I2& dest_begin, I2& dest_end,
        size_t half_length, double rank)
{
    // Types in use.
    typedef typename std::iterator_traits<I1>::value_type T1;
    typedef typename std::iterator_traits<I2>::value_type T2;
    typedef typename std::iterator_traits<I1>::difference_type I1_diff_t;
    typedef typename std::iterator_traits<I2>::difference_type I2_diff_t;

    // Establish common types to work with source and destination values.
    BOOST_STATIC_ASSERT((boost::is_same<T1, T2>::value));
    typedef T1 T;
    typedef typename boost::common_type<I1_diff_t, I2_diff_t>::type I_diff_t;

    // Define container types that will be used.
    typedef boost::container::multiset< T,
            std::less<T>,
            boost::container::node_allocator<T>,
            boost::container::tree_assoc_options< boost::container::tree_type<boost::container::scapegoat_tree> >::type> multiset;
    typedef std::deque< typename multiset::iterator > deque;

    // Ensure the result will fit.
    assert(std::distance(src_begin, src_end) <= std::distance(dest_begin, dest_end));

    // Window length cannot exceed input data with reflection.
    assert((half_length + 1) <= std::distance(src_begin, src_end));

    // Rank must be in the range 0 to 1.
    assert((0 <= rank) && (rank <= 1));

    // Track values in window both in sorted and sequential order.
    multiset sorted_window;
    deque window_iters(2 * half_length + 1);

    // Iterators in source and destination
    I1 src_pos = src_begin;
    I1 dest_pos = dest_begin;

    // Get the initial window in sequential order with reflection
    // Insert elements in order to the multiset for sorting.
    // Store all multiset iterators into the deque in sequential order.
    std::deque<T> window_init(2 * half_length + 1);
    window_init[half_length] = *(src_pos++);
    for (I_diff_t j = 1; j < half_length + 1; j++)
    {
        window_init[half_length - j] = *src_pos;
        window_init[half_length + j] = *src_pos;
        src_pos++;
    }
    for (I_diff_t j = 0; j < 2 * half_length + 1; j++)
    {
        window_iters[j] = sorted_window.insert(window_init.front());
        window_init.pop_front();
    }

    // Window position corresponding to this rank.
    const I_diff_t rank_pos = static_cast<I_diff_t>(boost::math::round(rank * (2 * half_length)));
    typename multiset::iterator rank_point = sorted_window.begin();

    // Move our selection point to the corresponding rank.
    for (I_diff_t i = 0; i < rank_pos; i++)
    {
        rank_point++;
    }

    // Roll window forward one value at a time.
    typename multiset::iterator prev_iter;
    T prev_value;
    T next_value;
    I_diff_t window_reflect_pos = 2 * half_length;
    while ( window_reflect_pos >= 0 )
    {
        *(dest_pos++) = *rank_point;

        prev_iter = window_iters.front();
        prev_value = *prev_iter;
        window_iters.pop_front();

	// Determine next value to add to window.
	// Handle special cases like reflection at the end.
        if ( src_pos == src_end )
        {
            if ( window_reflect_pos == 0 )
            {
                window_reflect_pos -= 2;
                next_value = prev_value;
            }
            else
            {
                window_reflect_pos -= 2;
                next_value = *(window_iters[window_reflect_pos]);
            }
        }
        else
        {
            next_value = *(src_pos++);
        }

	// Remove old value and add new value to the window.
	// Handle special cases where `rank_pos` may have an adjusted position
	// due to where the old and new values are inserted.
        if ( ( *rank_point < prev_value ) && ( *rank_point <= next_value ) )
        {
            sorted_window.erase(prev_iter);
            window_iters.push_back(sorted_window.insert(next_value));
        }
        else if ( ( *rank_point >= prev_value ) && ( *rank_point > next_value ) )
        {
            if ( rank_point == prev_iter )
            {
                window_iters.push_back(sorted_window.insert(next_value));
                rank_point--;

                sorted_window.erase(prev_iter);
            }
            else
            {
                sorted_window.erase(prev_iter);
                window_iters.push_back(sorted_window.insert(next_value));
            }
        }
        else if ( ( *rank_point < prev_value ) && ( *rank_point > next_value ) )
        {
            sorted_window.erase(prev_iter);
            window_iters.push_back(sorted_window.insert(next_value));

            rank_point--;
        }
        else if ( ( *rank_point >= prev_value ) && ( *rank_point <= next_value ) )
        {
            if (rank_point == prev_iter)
            {
                window_iters.push_back(sorted_window.insert(next_value));
                rank_point++;

                sorted_window.erase(prev_iter);
            }
            else
            {
                sorted_window.erase(prev_iter);
                window_iters.push_back(sorted_window.insert(next_value));

                rank_point++;
            }
        }
    }
}

}

namespace std
{
    template <class T, size_t N>
    ostream& operator<<(ostream& out, const boost::array<T, N>& that)
    {
        out << "{ ";
        for (unsigned int i = 0; i < (N - 1); i++)
        {
            out << that[i] << ", ";
        }
        out << that[N - 1] << " }";

        return(out);
    }
}

#endif //__RANK_FILTER__
<commit_msg>Use `std::advance` to get `rank_point`<commit_after>#ifndef __RANK_FILTER__
#define __RANK_FILTER__

#include <deque>
#include <cassert>
#include <functional>
#include <iostream>
#include <iterator>

#include <boost/array.hpp>
#include <boost/container/set.hpp>
#include <boost/container/node_allocator.hpp>
#include <boost/math/special_functions/round.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>

namespace rank_filter
{

template<class I1,
         class I2>
inline void lineRankOrderFilter1D(const I1& src_begin, const I1& src_end,
        I2& dest_begin, I2& dest_end,
        size_t half_length, double rank)
{
    // Types in use.
    typedef typename std::iterator_traits<I1>::value_type T1;
    typedef typename std::iterator_traits<I2>::value_type T2;
    typedef typename std::iterator_traits<I1>::difference_type I1_diff_t;
    typedef typename std::iterator_traits<I2>::difference_type I2_diff_t;

    // Establish common types to work with source and destination values.
    BOOST_STATIC_ASSERT((boost::is_same<T1, T2>::value));
    typedef T1 T;
    typedef typename boost::common_type<I1_diff_t, I2_diff_t>::type I_diff_t;

    // Define container types that will be used.
    typedef boost::container::multiset< T,
            std::less<T>,
            boost::container::node_allocator<T>,
            boost::container::tree_assoc_options< boost::container::tree_type<boost::container::scapegoat_tree> >::type> multiset;
    typedef std::deque< typename multiset::iterator > deque;

    // Ensure the result will fit.
    assert(std::distance(src_begin, src_end) <= std::distance(dest_begin, dest_end));

    // Window length cannot exceed input data with reflection.
    assert((half_length + 1) <= std::distance(src_begin, src_end));

    // Rank must be in the range 0 to 1.
    assert((0 <= rank) && (rank <= 1));

    // Track values in window both in sorted and sequential order.
    multiset sorted_window;
    deque window_iters(2 * half_length + 1);

    // Iterators in source and destination
    I1 src_pos = src_begin;
    I1 dest_pos = dest_begin;

    // Get the initial window in sequential order with reflection
    // Insert elements in order to the multiset for sorting.
    // Store all multiset iterators into the deque in sequential order.
    std::deque<T> window_init(2 * half_length + 1);
    window_init[half_length] = *(src_pos++);
    for (I_diff_t j = 1; j < half_length + 1; j++)
    {
        window_init[half_length - j] = *src_pos;
        window_init[half_length + j] = *src_pos;
        src_pos++;
    }
    for (I_diff_t j = 0; j < 2 * half_length + 1; j++)
    {
        window_iters[j] = sorted_window.insert(window_init.front());
        window_init.pop_front();
    }

    // Window position corresponding to this rank.
    const I_diff_t rank_pos = static_cast<I_diff_t>(boost::math::round(rank * (2 * half_length)));
    typename multiset::iterator rank_point = sorted_window.begin();
    std::advance(rank_point, rank_pos);

    // Roll window forward one value at a time.
    typename multiset::iterator prev_iter;
    T prev_value;
    T next_value;
    I_diff_t window_reflect_pos = 2 * half_length;
    while ( window_reflect_pos >= 0 )
    {
        *(dest_pos++) = *rank_point;

        prev_iter = window_iters.front();
        prev_value = *prev_iter;
        window_iters.pop_front();

	// Determine next value to add to window.
	// Handle special cases like reflection at the end.
        if ( src_pos == src_end )
        {
            if ( window_reflect_pos == 0 )
            {
                window_reflect_pos -= 2;
                next_value = prev_value;
            }
            else
            {
                window_reflect_pos -= 2;
                next_value = *(window_iters[window_reflect_pos]);
            }
        }
        else
        {
            next_value = *(src_pos++);
        }

	// Remove old value and add new value to the window.
	// Handle special cases where `rank_pos` may have an adjusted position
	// due to where the old and new values are inserted.
        if ( ( *rank_point < prev_value ) && ( *rank_point <= next_value ) )
        {
            sorted_window.erase(prev_iter);
            window_iters.push_back(sorted_window.insert(next_value));
        }
        else if ( ( *rank_point >= prev_value ) && ( *rank_point > next_value ) )
        {
            if ( rank_point == prev_iter )
            {
                window_iters.push_back(sorted_window.insert(next_value));
                rank_point--;

                sorted_window.erase(prev_iter);
            }
            else
            {
                sorted_window.erase(prev_iter);
                window_iters.push_back(sorted_window.insert(next_value));
            }
        }
        else if ( ( *rank_point < prev_value ) && ( *rank_point > next_value ) )
        {
            sorted_window.erase(prev_iter);
            window_iters.push_back(sorted_window.insert(next_value));

            rank_point--;
        }
        else if ( ( *rank_point >= prev_value ) && ( *rank_point <= next_value ) )
        {
            if (rank_point == prev_iter)
            {
                window_iters.push_back(sorted_window.insert(next_value));
                rank_point++;

                sorted_window.erase(prev_iter);
            }
            else
            {
                sorted_window.erase(prev_iter);
                window_iters.push_back(sorted_window.insert(next_value));

                rank_point++;
            }
        }
    }
}

}

namespace std
{
    template <class T, size_t N>
    ostream& operator<<(ostream& out, const boost::array<T, N>& that)
    {
        out << "{ ";
        for (unsigned int i = 0; i < (N - 1); i++)
        {
            out << that[i] << ", ";
        }
        out << that[N - 1] << " }";

        return(out);
    }
}

#endif //__RANK_FILTER__
<|endoftext|>


<commit_before>#pragma once

#include "simlib/concat.hh"
#include "simlib/concat_tostr.hh"
#include "simlib/inplace_array.hh"
#include "simlib/logger.hh"

#include <cstdlib>
#include <exception>

#define eprintf(...) fprintf(stderr, __VA_ARGS__)

#ifdef DEBUG
#define D(...) __VA_ARGS__
#define ND(...)
#else
#define D(...)
#define ND(...) __VA_ARGS__
#endif

#define STRINGIZE2(x) #x
#define STRINGIZE(x) STRINGIZE2(x)

#define LOG_LINE stdlog(__FILE__ ":" STRINGIZE(__LINE__))

#define E(...) eprintf(__VA_ARGS__)

#define throw_assert(expr)                                                     \
	((expr) ? (void)0                                                          \
	        : throw std::runtime_error(concat_tostr(                           \
	             __FILE__ ":" STRINGIZE(__LINE__) ": ", __PRETTY_FUNCTION__,   \
	             ": Assertion `" #expr "` failed.")))

// Very useful - includes exception origin
#define THROW(...)                                                             \
	throw std::runtime_error(concat_tostr(                                     \
	   __VA_ARGS__, " (thrown at " __FILE__ ":" STRINGIZE(__LINE__) ")"))

namespace simlib_debug {

constexpr bool is_va_empty() { return true; }

template <class T1, class... T>
constexpr bool is_va_empty(T1&& /*unused*/, T&&... /*unused*/) {
	return false;
}

constexpr const char* what_of() { return ""; }

inline const char* what_of(const std::exception& e) { return e.what(); }

} // namespace simlib_debug

inline InplaceBuff<4096> errmsg(int errnum) noexcept {
	static_assert(
	   to_string(std::numeric_limits<decltype(errnum)>::max()).size() + 1 <
	      90, // +1 for minus a sign
	   "Needed to fit error message in the returned buffer");
	std::array<char, 4000> buff{};
	return concat<4096>(" - ", errnum, ": ",
	                    strerror_r(errnum, buff.data(), buff.size()));
}

inline auto errmsg() noexcept { return errmsg(errno); }

namespace stack_unwinding {

class StackGuard {
	static inline thread_local uintmax_t event_stamp;

	bool inside_catch_ = static_cast<bool>(std::current_exception());
	int uncaught_counter_ = std::uncaught_exceptions();
	uintmax_t creation_stamp_ = event_stamp++;
	const char* file_;
	size_t line_;
	const char* pretty_func_;

public:
	struct StackMark {
		InplaceBuff<512> description;
		uintmax_t stamp = event_stamp++;

		template <class... Args,
		          std::enable_if_t<(is_string_argument<Args> and ...), int> = 0>
		explicit StackMark(Args&&... args)
		: description(std::in_place, std::forward<Args>(args)...) {}
	};

	static inline thread_local InplaceArray<StackMark, 128> marks_collected;

	StackGuard(const char* file, size_t line,
	           const char* pretty_function) noexcept
	: file_(file)
	, line_(line)
	, pretty_func_(pretty_function) {}

	StackGuard(const StackGuard&) = delete;
	StackGuard(StackGuard&&) = delete;
	StackGuard& operator=(const StackGuard&) = delete;
	StackGuard& operator=(StackGuard&&) = delete;

	~StackGuard() {
		if (std::uncaught_exceptions() == 1 and uncaught_counter_ == 0 and
		    not inside_catch_)
		{
			// Remove stack marks that are from earlier exceptions
			if (marks_collected.size() > 0 and
			    marks_collected.back().stamp < creation_stamp_)
			{
				marks_collected.clear();
			}

			try {
				StackMark a;
				StackMark b;
				assert(a.stamp + 1 == b.stamp);
				marks_collected.emplace_back(pretty_func_, " at ", file_, ':',
				                             line_);
			} catch (...) {
				// Nothing we can do here
			}
		}
	}
};

} // namespace stack_unwinding

#define STACK_UNWINDING_MARK_CONCATENATE_DETAIL(x, y) x##y
#define STACK_UNWINDING_MARK_CONCAT(x, y)                                      \
	STACK_UNWINDING_MARK_CONCATENATE_DETAIL(x, y)

#define STACK_UNWINDING_MARK                                                   \
	::stack_unwinding::StackGuard STACK_UNWINDING_MARK_CONCAT(                 \
	   stack_unwind_mark_no_, __COUNTER__)(__FILE__, __LINE__,                 \
	                                       __PRETTY_FUNCTION__)

#define ERRLOG_CATCH(...)                                                      \
	do {                                                                       \
		auto tmplog =                                                          \
		   errlog(__FILE__ ":" STRINGIZE(__LINE__) ": Caught exception",       \
		          ::simlib_debug::is_va_empty(__VA_ARGS__) ? "" : " -> ",      \
		          ::simlib_debug::what_of(__VA_ARGS__),                        \
		          "\nStack unwinding marks:\n");                               \
                                                                               \
		size_t i = 0;                                                          \
		for (auto const& mark :                                                \
		     ::stack_unwinding::StackGuard::marks_collected)                   \
			tmplog('[', i++, "] ", mark.description, "\n");                    \
                                                                               \
		tmplog.flush_no_nl();                                                  \
		::stack_unwinding::StackGuard::marks_collected.clear();                \
	} while (false)

class ThrowingIsBug {
	bool thrown = true;

public:
	template <class T>
	decltype(auto) evaluate(T&& expr) noexcept {
		thrown = false;
		return std::forward<T>(expr);
	}

	ThrowingIsBug() = default;

	ThrowingIsBug(const ThrowingIsBug&) = delete;
	ThrowingIsBug(ThrowingIsBug&&) = delete;
	ThrowingIsBug& operator=(const ThrowingIsBug&) = delete;
	ThrowingIsBug& operator=(ThrowingIsBug&&) = delete;

	~ThrowingIsBug() {
		if (thrown) {
			errlog("BUG: this was expected to not throw");
			std::abort();
		}
	}
};

// Sadly it won't preserve prvalue-ness, a prvalue will become an xvalue
#define WONT_THROW(...) (ThrowingIsBug().evaluate(__VA_ARGS__))
<commit_msg>debug.hh: added DebugLogger<commit_after>#pragma once

#include "simlib/concat.hh"
#include "simlib/concat_tostr.hh"
#include "simlib/inplace_array.hh"
#include "simlib/logger.hh"

#include <cstdlib>
#include <exception>

#define eprintf(...) fprintf(stderr, __VA_ARGS__)

#ifdef DEBUG
#define D(...) __VA_ARGS__
#define ND(...)
#else
#define D(...)
#define ND(...) __VA_ARGS__
#endif

#define STRINGIZE2(x) #x
#define STRINGIZE(x) STRINGIZE2(x)

#define LOG_LINE stdlog(__FILE__ ":" STRINGIZE(__LINE__))

#define E(...) eprintf(__VA_ARGS__)

#define throw_assert(expr)                                                     \
	((expr) ? (void)0                                                          \
	        : throw std::runtime_error(concat_tostr(                           \
	             __FILE__ ":" STRINGIZE(__LINE__) ": ", __PRETTY_FUNCTION__,   \
	             ": Assertion `" #expr "` failed.")))

// Very useful - includes exception origin
#define THROW(...)                                                             \
	throw std::runtime_error(concat_tostr(                                     \
	   __VA_ARGS__, " (thrown at " __FILE__ ":" STRINGIZE(__LINE__) ")"))

namespace simlib_debug {

constexpr bool is_va_empty() { return true; }

template <class T1, class... T>
constexpr bool is_va_empty(T1&& /*unused*/, T&&... /*unused*/) {
	return false;
}

constexpr const char* what_of() { return ""; }

inline const char* what_of(const std::exception& e) { return e.what(); }

} // namespace simlib_debug

inline InplaceBuff<4096> errmsg(int errnum) noexcept {
	static_assert(
	   to_string(std::numeric_limits<decltype(errnum)>::max()).size() + 1 <
	      90, // +1 for minus a sign
	   "Needed to fit error message in the returned buffer");
	std::array<char, 4000> buff{};
	return concat<4096>(" - ", errnum, ": ",
	                    strerror_r(errnum, buff.data(), buff.size()));
}

inline auto errmsg() noexcept { return errmsg(errno); }

namespace stack_unwinding {

class StackGuard {
	static inline thread_local uintmax_t event_stamp;

	bool inside_catch_ = static_cast<bool>(std::current_exception());
	int uncaught_counter_ = std::uncaught_exceptions();
	uintmax_t creation_stamp_ = event_stamp++;
	const char* file_;
	size_t line_;
	const char* pretty_func_;

public:
	struct StackMark {
		InplaceBuff<512> description;
		uintmax_t stamp = event_stamp++;

		template <class... Args,
		          std::enable_if_t<(is_string_argument<Args> and ...), int> = 0>
		explicit StackMark(Args&&... args)
		: description(std::in_place, std::forward<Args>(args)...) {}
	};

	static inline thread_local InplaceArray<StackMark, 128> marks_collected;

	StackGuard(const char* file, size_t line,
	           const char* pretty_function) noexcept
	: file_(file)
	, line_(line)
	, pretty_func_(pretty_function) {}

	StackGuard(const StackGuard&) = delete;
	StackGuard(StackGuard&&) = delete;
	StackGuard& operator=(const StackGuard&) = delete;
	StackGuard& operator=(StackGuard&&) = delete;

	~StackGuard() {
		if (std::uncaught_exceptions() == 1 and uncaught_counter_ == 0 and
		    not inside_catch_)
		{
			// Remove stack marks that are from earlier exceptions
			if (marks_collected.size() > 0 and
			    marks_collected.back().stamp < creation_stamp_)
			{
				marks_collected.clear();
			}

			try {
				StackMark a;
				StackMark b;
				assert(a.stamp + 1 == b.stamp);
				marks_collected.emplace_back(pretty_func_, " at ", file_, ':',
				                             line_);
			} catch (...) {
				// Nothing we can do here
			}
		}
	}
};

} // namespace stack_unwinding

#define STACK_UNWINDING_MARK_CONCATENATE_DETAIL(x, y) x##y
#define STACK_UNWINDING_MARK_CONCAT(x, y)                                      \
	STACK_UNWINDING_MARK_CONCATENATE_DETAIL(x, y)

#define STACK_UNWINDING_MARK                                                   \
	::stack_unwinding::StackGuard STACK_UNWINDING_MARK_CONCAT(                 \
	   stack_unwind_mark_no_, __COUNTER__)(__FILE__, __LINE__,                 \
	                                       __PRETTY_FUNCTION__)

#define ERRLOG_CATCH(...)                                                      \
	do {                                                                       \
		auto tmplog =                                                          \
		   errlog(__FILE__ ":" STRINGIZE(__LINE__) ": Caught exception",       \
		          ::simlib_debug::is_va_empty(__VA_ARGS__) ? "" : " -> ",      \
		          ::simlib_debug::what_of(__VA_ARGS__),                        \
		          "\nStack unwinding marks:\n");                               \
                                                                               \
		size_t i = 0;                                                          \
		for (auto const& mark :                                                \
		     ::stack_unwinding::StackGuard::marks_collected)                   \
			tmplog('[', i++, "] ", mark.description, "\n");                    \
                                                                               \
		tmplog.flush_no_nl();                                                  \
		::stack_unwinding::StackGuard::marks_collected.clear();                \
	} while (false)

class ThrowingIsBug {
	bool thrown = true;

public:
	template <class T>
	decltype(auto) evaluate(T&& expr) noexcept {
		thrown = false;
		return std::forward<T>(expr);
	}

	ThrowingIsBug() = default;

	ThrowingIsBug(const ThrowingIsBug&) = delete;
	ThrowingIsBug(ThrowingIsBug&&) = delete;
	ThrowingIsBug& operator=(const ThrowingIsBug&) = delete;
	ThrowingIsBug& operator=(ThrowingIsBug&&) = delete;

	~ThrowingIsBug() {
		if (thrown) {
			errlog("BUG: this was expected to not throw");
			std::abort();
		}
	}
};

// Sadly it won't preserve prvalue-ness, a prvalue will become an xvalue
#define WONT_THROW(...) (ThrowingIsBug().evaluate(__VA_ARGS__))

// E.g. use:
// static DebugLogger<true> debuglog;
// debuglog("sth: ", sth);
template <bool enable>
struct DebugLogger {
	static constexpr bool enabled = enable;

	template <class... Args>
	void operator()(Args&&... args) const {
		if constexpr (enabled) {
			stdlog(args...);
		}
	}
};
<|endoftext|>


<commit_before>#pragma once
#ifndef _SDL_UTILS_H
#define _SDL_UTILS_H
#include <iostream>
#include <string>
#include <SDL.h>

namespace Snake {
  class Utils {
    public:
      static void logSDLError(std::ostream &os, const std::string &msg) {
        os << msg << " Error: " << SDL_GetError() << std::endl;
      }
      static void debug(const std::string &msg) {
        std::cout << "Debug: " << msg << std::endl;
      }
  };
}
#endif
<commit_msg>Print debugging output only when DEBUG=1 is passed<commit_after>#pragma once
#include <iostream>
#include <string>
#include <SDL.h>

namespace Snake {
  class Utils {
    public:
      static void logSDLError(std::ostream &os, const std::string &msg) {
        os << msg << " Error: " << SDL_GetError() << std::endl;
      }
      static void debug(const std::string &msg) {
        bool shouldDebug = false;
        char* debugEnv = std::getenv("DEBUG");
        if (debugEnv != NULL && strcmp(debugEnv, "1") == 0) {
          shouldDebug = true;
        }

        if (shouldDebug) {
          std::cout << "Debug: " << msg << std::endl;
        }
      }
  };
}
<|endoftext|>


<commit_before>/**
 * Zillians MMO
 * Copyright (C) 2007-2011 Zillians.com, Inc.
 * For more information see http://www.zillians.com
 *
 * Zillians MMO is the library and runtime for massive multiplayer online game
 * development in utility computing model, which runs as a service for every
 * developer to build their virtual world running on our GPU-assisted machines.
 *
 * This is a close source library intended to be used solely within Zillians.com
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "language/stage/parser/ThorScriptParserStage.h"
#include "language/context/ParserContext.h"
#include "language/grammar/ThorScript.h"
#include "language/action/SemanticActions.h"
#include "language/tree/visitor/general/PrettyPrintVisitor.h"
#include "language/ThorScriptCompiler.h"
#include "utility/Foreach.h"
#include "utility/UnicodeUtil.h"

namespace classic = boost::spirit::classic;
namespace qi = boost::spirit::qi;

namespace zillians { namespace language { namespace stage {

namespace {

// since '\t' may be printed in spaces and I don't know a way to change std::wcout, we simply replace the tab with desired number of spaces
// so that we can have correct error pointing cursor
// (note that because '\t' equals to 4 spaces reported by spirit, we have to make sure it's printed in the exact same way)
static void expand_tabs(const std::wstring& input, std::wstring& output, int number_of_space_for_tab = 4)
{
	for(std::wstring::const_iterator it = input.begin(); it != input.end(); ++it)
	{
		if(*it == '\t')
			for(int i=0;i<number_of_space_for_tab;++i) output.push_back(L' ');
		else
			output.push_back(*it);
	}
}

}

ThorScriptParserStage::ThorScriptParserStage() : skip_parse(false), dump_parse(false)
{ }

ThorScriptParserStage::~ThorScriptParserStage()
{ }

const char* ThorScriptParserStage::name()
{
	return "thorscript_parser_stage";
}

void ThorScriptParserStage::initializeOptions(po::options_description& option_desc, po::positional_options_description& positional_desc)
{
    option_desc.add_options()
	("skip-parse",                                      "skip parsing stage and use provided parser context")
    ("dump-parse",										"dump parse tree for debugging purpose")
    ("input,i", po::value<std::vector<std::string>>(),	"thorscript files");

    positional_desc.add("input", -1);
}

bool ThorScriptParserStage::parseOptions(po::variables_map& vm)
{
	skip_parse = (vm.count("skip-parse") > 0);
	dump_parse = (vm.count("dump-parse") > 0);

	if(vm.count("input") > 0)
	{
		inputs = vm["input"].as<std::vector<std::string>>();
		return true;
	}
	else
	{
		return false;
	}
}

bool ThorScriptParserStage::execute(bool& continue_execution)
{
	if(skip_parse)
		return true;

	setParserContext(new ParserContext());

	if(inputs.size() > 0)
	{
		foreach(i, inputs)
		{
			if(!parse(*i)) return false;
		}

		return true;
	}
	else
	{
		return true;
	}
}

bool ThorScriptParserStage::parse(std::string filename)
{
	std::ifstream in(filename, std::ios_base::in);

	if(!in.good())
	{
		LOG4CXX_ERROR(Logger::ParserStage, "failed to open file: " << filename);
		return false;
	}

    // ignore the BOM marking the beginning of a UTF-8 file in Windows
    if(in.peek() == '\xef')
    {
        char s[3];
        in >> s[0] >> s[1] >> s[2];
        s[3] = '\0';
        if (s != std::string("\xef\xbb\xbf"))
        {
            std::cerr << "parser error: unexpected characters from input file: " << filename << std::endl;
            return false;
        }
    }

    std::string source_code_raw;
	in.unsetf(std::ios::skipws); // disable white space skipping
	std::copy(std::istream_iterator<char>(in), std::istream_iterator<char>(), std::back_inserter(source_code_raw));

    // convert it from UTF8 into UCS4 as a string by using u8_to_u32_iterator
    std::wstring source_code;
	utf8_to_ucs4(source_code_raw, source_code);

    // enable correct locale so that we can print UCS4 characters
    enable_default_locale(std::wcout);

    getParserContext().enable_debug_parser = dump_parse;
    getParserContext().enable_semantic_action = !dump_parse;
    getParserContext().debug.current_source_index = ModuleSourceInfoContext::get(getParserContext().program)->addSource(filename);

    // try to parse
	typedef classic::position_iterator2<std::wstring::iterator> pos_iterator_type;
	try
	{
		pos_iterator_type begin(source_code.begin(), source_code.end(), s_to_ws(filename));
		pos_iterator_type end;

		grammar::ThorScript<pos_iterator_type, action::ThorScriptTreeAction> parser;
		grammar::detail::WhiteSpace<pos_iterator_type> skipper;

		if(!qi::phrase_parse(
				begin, end,
				parser,
				skipper))
		{
			return false;
		}
	}
	catch (const qi::expectation_failure<pos_iterator_type>& e)
	{
		const classic::file_position_base<std::wstring>& pos = e.first.get_position();

		// TODO output error using Logger
		std::wstring current_line;
		expand_tabs(e.first.get_currentline(), current_line);
		std::wcerr << L"parse error at file " << pos.file << L" line " << pos.line
				<< L" column " << pos.column << std::endl
				<< L"'" << current_line << L"'" << std::endl
				<< std::setw(pos.column) << L" " << L"^- here" << std::endl;

		return false;
	}

	return true;
}

} } }

<commit_msg>fix uninitialized ModuleSourceInfoContext<commit_after>/**
 * Zillians MMO
 * Copyright (C) 2007-2011 Zillians.com, Inc.
 * For more information see http://www.zillians.com
 *
 * Zillians MMO is the library and runtime for massive multiplayer online game
 * development in utility computing model, which runs as a service for every
 * developer to build their virtual world running on our GPU-assisted machines.
 *
 * This is a close source library intended to be used solely within Zillians.com
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "language/stage/parser/ThorScriptParserStage.h"
#include "language/context/ParserContext.h"
#include "language/grammar/ThorScript.h"
#include "language/action/SemanticActions.h"
#include "language/tree/visitor/general/PrettyPrintVisitor.h"
#include "language/ThorScriptCompiler.h"
#include "utility/Foreach.h"
#include "utility/UnicodeUtil.h"

namespace classic = boost::spirit::classic;
namespace qi = boost::spirit::qi;

namespace zillians { namespace language { namespace stage {

namespace {

// since '\t' may be printed in spaces and I don't know a way to change std::wcout, we simply replace the tab with desired number of spaces
// so that we can have correct error pointing cursor
// (note that because '\t' equals to 4 spaces reported by spirit, we have to make sure it's printed in the exact same way)
static void expand_tabs(const std::wstring& input, std::wstring& output, int number_of_space_for_tab = 4)
{
	for(std::wstring::const_iterator it = input.begin(); it != input.end(); ++it)
	{
		if(*it == '\t')
			for(int i=0;i<number_of_space_for_tab;++i) output.push_back(L' ');
		else
			output.push_back(*it);
	}
}

}

ThorScriptParserStage::ThorScriptParserStage() : skip_parse(false), dump_parse(false)
{ }

ThorScriptParserStage::~ThorScriptParserStage()
{ }

const char* ThorScriptParserStage::name()
{
	return "thorscript_parser_stage";
}

void ThorScriptParserStage::initializeOptions(po::options_description& option_desc, po::positional_options_description& positional_desc)
{
    option_desc.add_options()
	("skip-parse",                                      "skip parsing stage and use provided parser context")
    ("dump-parse",										"dump parse tree for debugging purpose")
    ("input,i", po::value<std::vector<std::string>>(),	"thorscript files");

    positional_desc.add("input", -1);
}

bool ThorScriptParserStage::parseOptions(po::variables_map& vm)
{
	skip_parse = (vm.count("skip-parse") > 0);
	dump_parse = (vm.count("dump-parse") > 0);

	if(vm.count("input") > 0)
	{
		inputs = vm["input"].as<std::vector<std::string>>();
		return true;
	}
	else
	{
		return false;
	}
}

bool ThorScriptParserStage::execute(bool& continue_execution)
{
	if(skip_parse)
		return true;

	// prepare the global parser context
	setParserContext(new ParserContext());

	// prepare the module source info context for the root program node
   	ModuleSourceInfoContext::set(getParserContext().program, new ModuleSourceInfoContext());

	if(inputs.size() > 0)
	{
		foreach(i, inputs)
		{
			if(!parse(*i)) return false;
		}

		return true;
	}
	else
	{
		return true;
	}
}

bool ThorScriptParserStage::parse(std::string filename)
{
	std::ifstream in(filename, std::ios_base::in);

	if(!in.good())
	{
		LOG4CXX_ERROR(Logger::ParserStage, "failed to open file: " << filename);
		return false;
	}

    // ignore the BOM marking the beginning of a UTF-8 file in Windows
    if(in.peek() == '\xef')
    {
        char s[3];
        in >> s[0] >> s[1] >> s[2];
        s[3] = '\0';
        if (s != std::string("\xef\xbb\xbf"))
        {
            std::cerr << "parser error: unexpected characters from input file: " << filename << std::endl;
            return false;
        }
    }

    std::string source_code_raw;
	in.unsetf(std::ios::skipws); // disable white space skipping
	std::copy(std::istream_iterator<char>(in), std::istream_iterator<char>(), std::back_inserter(source_code_raw));

    // convert it from UTF8 into UCS4 as a string by using u8_to_u32_iterator
    std::wstring source_code;
	utf8_to_ucs4(source_code_raw, source_code);

    // enable correct locale so that we can print UCS4 characters
    enable_default_locale(std::wcout);

    getParserContext().enable_debug_parser = dump_parse;
    getParserContext().enable_semantic_action = !dump_parse;
    getParserContext().debug.current_source_index = ModuleSourceInfoContext::get(getParserContext().program)->addSource(filename);

    // try to parse
	typedef classic::position_iterator2<std::wstring::iterator> pos_iterator_type;
	try
	{
		pos_iterator_type begin(source_code.begin(), source_code.end(), s_to_ws(filename));
		pos_iterator_type end;

		grammar::ThorScript<pos_iterator_type, action::ThorScriptTreeAction> parser;
		grammar::detail::WhiteSpace<pos_iterator_type> skipper;

		if(!qi::phrase_parse(
				begin, end,
				parser,
				skipper))
		{
			return false;
		}
	}
	catch (const qi::expectation_failure<pos_iterator_type>& e)
	{
		const classic::file_position_base<std::wstring>& pos = e.first.get_position();

		// TODO output error using Logger
		std::wstring current_line;
		expand_tabs(e.first.get_currentline(), current_line);
		std::wcerr << L"parse error at file " << pos.file << L" line " << pos.line
				<< L" column " << pos.column << std::endl
				<< L"'" << current_line << L"'" << std::endl
				<< std::setw(pos.column) << L" " << L"^- here" << std::endl;

		return false;
	}

	return true;
}

} } }

<|endoftext|>


<commit_before>/* Copyright 2016-2017 CyberTech Labs Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. */

#include <QtCore/QJsonDocument>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonObject>
#include <QtCore/QFile>
#include <QtCore/QRegularExpression>

#include "trikKitInterpreterCommon/trikTextualInterpreter.h"

#include <qrgui/plugins/toolPluginInterface/usedInterfaces/errorReporterInterface.h>

#include <twoDModel/engine/model/timeline.h>

Q_DECLARE_METATYPE(utils::AbstractTimer*)

const QString jsOverrides = "script.random = brick.random;script.wait = brick.wait;script.time = brick.time;"
		"script.readAll = brick.readAll;script.timer = brick.timer;"
		"arrayPPinternal = function(arg) {"
			"var res = '[';"
			"for(var i = 0; i < arg.length; i++) {"
				"var separator = i + 1 != arg.length ? ', ' : '';"
				"if (arg[i] instanceof Array) {"
					"res += arrayPPinternal(arg[i]) + separator;"
				"} else { res += arg[i].toString() + separator; }"
			"}"
			"res += ']';"
			"return res;"
		"};"
		"print = function() "
		"{"
			"var res = '';"
			"for(var i = 0; i < arguments.length; i++) {"
				"if (arguments[i] instanceof Array) {res += arrayPPinternal(arguments[i]);"
				"} else {res += arguments[i].toString();}"
			"};"
			"brick.log(res);"
			"return res;"
		"};"
		"script.system = function() {print('system is disabled in the interpreter');};";

const QString pyOverrides ="\ndef print(args): brick.log(args);\n";

trik::TrikTextualInterpreter::TrikTextualInterpreter(const QSharedPointer<trik::robotModel::twoD::TrikTwoDRobotModel> &model)
	: mRunning(false), mBrick(model), mScriptRunner(mBrick, nullptr), mErrorReporter(nullptr)
{
	connect(&mBrick, &TrikBrick::error, this, &TrikTextualInterpreter::reportError);
	connect(&mBrick, &TrikBrick::warning, this, &TrikTextualInterpreter::reportWarning);

	auto atimerToScriptValue = [](QScriptEngine *engine, utils::AbstractTimer* const &in){
		return engine->newQObject(in);
	};
	auto atimerFromScriptValue = [](const QScriptValue &object, utils::AbstractTimer* &out){
		out = qobject_cast<utils::AbstractTimer*>(object.toQObject());
	};
	mScriptRunner.addCustomEngineInitStep([&atimerToScriptValue, &atimerFromScriptValue](QScriptEngine *engine){
		qScriptRegisterMetaType<utils::AbstractTimer*>(engine, atimerToScriptValue, atimerFromScriptValue);
	});
	connect(&mScriptRunner, SIGNAL(completed(QString,int)), this, SLOT(scriptFinished(QString,int)));
}

trik::TrikTextualInterpreter::~TrikTextualInterpreter()
{
	abort();
}

void trik::TrikTextualInterpreter::interpretCommand(const QString &script)
{
	mScriptRunner.runDirectCommand(script);
}

void trik::TrikTextualInterpreter::interpretScript(const QString &script, const QString &languageExtension)
{
	mRunning = true;
	mBrick.processSensors(true);
	if (languageExtension.contains("js")) {
		mScriptRunner.run(jsOverrides + script);
	} else if (languageExtension.contains("py")) {
		QString updatedScript = script;
		int lastIndexOfImport = updatedScript.lastIndexOf(QRegularExpression("^import .*"
				, QRegularExpression::MultilineOption));
		int indexOf = updatedScript.indexOf(QRegularExpression("$", QRegularExpression::MultilineOption)
				, lastIndexOfImport);
		updatedScript.insert(indexOf + 1, pyOverrides);
		mScriptRunner.run(updatedScript, "dummyFile.py");
	} else {
		Q_ASSERT(false);
	}
}

void trik::TrikTextualInterpreter::interpretScriptExercise(const QString &script
		, const QString &inputs, const QString &languageExtension)
{
	Q_UNUSED(languageExtension)
	mRunning = true;
	mBrick.processSensors(true);
	mBrick.setCurrentInputs(inputs);
	QString newScript = jsOverrides + "script.writeToFile = null;\n" + script;
	//qDebug() << newScript;
	mScriptRunner.run(newScript);
}

void trik::TrikTextualInterpreter::abort()
{
	//mScriptRunner.abort();
	mBrick.stopWaiting();
	Q_ASSERT(mScriptRunner.thread() == thread());
	QMetaObject::invokeMethod(&mScriptRunner, "abort", Qt::QueuedConnection); // just a wild test
	mRunning = false; // reset brick?
	mBrick.processSensors(false);
}

void trik::TrikTextualInterpreter::init()
{
	mBrick.init(); // very crucial. Maybe move into interpret methods?
}

void trik::TrikTextualInterpreter::setErrorReporter(qReal::ErrorReporterInterface &errorReporter)
{
	mErrorReporter = &errorReporter;
}

bool trik::TrikTextualInterpreter::isRunning() const
{
	return mRunning;
}

void trik::TrikTextualInterpreter::reportError(const QString &msg)
{
	mErrorReporter->addError(msg);
	//	mBrick.abort(); what if there are more errors?
}

void trik::TrikTextualInterpreter::reportWarning(const QString &msg)
{
	mErrorReporter->addWarning(msg);
}

void trik::TrikTextualInterpreter::reportLog(const QString &msg)
{
	mErrorReporter->addInformation("log: " + msg);
}

QString trik::TrikTextualInterpreter::initInputs(const QString &inputs) const
{
	auto jsValToStr = [this](const QJsonValue &val) -> QString {
		switch (val.type()) {
		case QJsonValue::Bool:
			return val.toBool() ? "true" : "false";
		case QJsonValue::Double:
			return QString::number(val.Double);//maybe increase precision
		case QJsonValue::String:
			return QString("\"%1\"").arg(val.toString());
		case QJsonValue::Array:
			return QString(QJsonDocument(val.toArray()).toJson(QJsonDocument::Compact));
		case QJsonValue::Object:
			return QString(QJsonDocument(val.toObject()).toJson(QJsonDocument::Compact));
		default:
			mErrorReporter->addError(QObject::tr("Bogus input values"));
			return "";
		}
	};

	QString result;
	if (!inputs.isEmpty()) {
		QString val;
		QFile file;
		file.setFileName(inputs);
		if (file.open(QIODevice::ReadOnly)) {
			val = file.readAll();
			file.close();
			QJsonDocument document = QJsonDocument::fromJson(val.toUtf8());
			QJsonObject object = document.object();
			for (const QString &name : object.keys()) {
				QJsonValue value = object[name];
				QString valueStr = jsValToStr(value);
				QString line("var " + name + " = " + valueStr + ";\n");
				result.append(line);
			}
		} else {
			mErrorReporter->addError(QObject::tr("Error: File %1 couldn't be opened!").arg(inputs));
		}
	}
	return result;
}

void trik::TrikTextualInterpreter::setRunning(bool running)
{
	mRunning = running;
	mBrick.processSensors(running);
}

void trik::TrikTextualInterpreter::setCurrentDir(const QString &dir)
{
	mBrick.setCurrentDir(dir);
}

QStringList trik::TrikTextualInterpreter::supportedRobotModelNames() const
{
	return {"TwoDRobotModelForTrikV62RealRobotModel", "TwoDRobotModelForTrikV6RealRobotModel"};
}

QStringList trik::TrikTextualInterpreter::knownMethodNames() const
{
	return mScriptRunner.knownMethodNames();
}

void trik::TrikTextualInterpreter::reinitRobotsParts()
{
	mBrick.reinitImitationCamera();
}

void trik::TrikTextualInterpreter::scriptFinished(const QString &error, int scriptId)
{
	Q_UNUSED(scriptId);
	if (!error.isEmpty()) {
		reportError(error);
	}

	if (mRunning) { /// @todo: figure out better place for this check - it should avoid double aborts
		mRunning = false;
		mBrick.processSensors(false);
		emit completed();
	}
}
<commit_msg>make vera happy<commit_after>/* Copyright 2016-2017 CyberTech Labs Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. */

#include <QtCore/QJsonDocument>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonObject>
#include <QtCore/QFile>
#include <QtCore/QRegularExpression>

#include "trikKitInterpreterCommon/trikTextualInterpreter.h"

#include <qrgui/plugins/toolPluginInterface/usedInterfaces/errorReporterInterface.h>

#include <twoDModel/engine/model/timeline.h>

Q_DECLARE_METATYPE(utils::AbstractTimer*)

const QString jsOverrides = "script.random = brick.random;script.wait = brick.wait;script.time = brick.time;"
		"script.readAll = brick.readAll;script.timer = brick.timer;"
		"arrayPPinternal = function(arg) {"
			"var res = '[';"
			"for(var i = 0; i < arg.length; i++) {"
				"var separator = i + 1 != arg.length ? ', ' : '';"
				"if (arg[i] instanceof Array) {"
					"res += arrayPPinternal(arg[i]) + separator;"
				"} else { res += arg[i].toString() + separator; }"
			"}"
			"res += ']';"
			"return res;"
		"};"
		"print = function() "
		"{"
			"var res = '';"
			"for(var i = 0; i < arguments.length; i++) {"
				"if (arguments[i] instanceof Array) {res += arrayPPinternal(arguments[i]);"
				"} else {res += arguments[i].toString();}"
			"};"
			"brick.log(res);"
			"return res;"
		"};"
		"script.system = function() {print('system is disabled in the interpreter');};";

const QString pyOverrides ="\ndef print(args): brick.log(args);\n";

trik::TrikTextualInterpreter::TrikTextualInterpreter(
		const QSharedPointer<trik::robotModel::twoD::TrikTwoDRobotModel> &model)
	: mRunning(false), mBrick(model), mScriptRunner(mBrick, nullptr), mErrorReporter(nullptr)
{
	connect(&mBrick, &TrikBrick::error, this, &TrikTextualInterpreter::reportError);
	connect(&mBrick, &TrikBrick::warning, this, &TrikTextualInterpreter::reportWarning);

	auto atimerToScriptValue = [](QScriptEngine *engine, utils::AbstractTimer* const &in){
		return engine->newQObject(in);
	};
	auto atimerFromScriptValue = [](const QScriptValue &object, utils::AbstractTimer* &out){
		out = qobject_cast<utils::AbstractTimer*>(object.toQObject());
	};
	mScriptRunner.addCustomEngineInitStep([&atimerToScriptValue, &atimerFromScriptValue](QScriptEngine *engine){
		qScriptRegisterMetaType<utils::AbstractTimer*>(engine, atimerToScriptValue, atimerFromScriptValue);
	});
	connect(&mScriptRunner, SIGNAL(completed(QString,int)), this, SLOT(scriptFinished(QString,int)));
}

trik::TrikTextualInterpreter::~TrikTextualInterpreter()
{
	abort();
}

void trik::TrikTextualInterpreter::interpretCommand(const QString &script)
{
	mScriptRunner.runDirectCommand(script);
}

void trik::TrikTextualInterpreter::interpretScript(const QString &script, const QString &languageExtension)
{
	mRunning = true;
	mBrick.processSensors(true);
	if (languageExtension.contains("js")) {
		mScriptRunner.run(jsOverrides + script);
	} else if (languageExtension.contains("py")) {
		QString updatedScript = script;
		int lastIndexOfImport = updatedScript.lastIndexOf(QRegularExpression("^import .*"
				, QRegularExpression::MultilineOption));
		int indexOf = updatedScript.indexOf(QRegularExpression("$", QRegularExpression::MultilineOption)
				, lastIndexOfImport);
		updatedScript.insert(indexOf + 1, pyOverrides);
		mScriptRunner.run(updatedScript, "dummyFile.py");
	} else {
		Q_ASSERT(false);
	}
}

void trik::TrikTextualInterpreter::interpretScriptExercise(const QString &script
		, const QString &inputs, const QString &languageExtension)
{
	Q_UNUSED(languageExtension)
	mRunning = true;
	mBrick.processSensors(true);
	mBrick.setCurrentInputs(inputs);
	QString newScript = jsOverrides + "script.writeToFile = null;\n" + script;
	//qDebug() << newScript;
	mScriptRunner.run(newScript);
}

void trik::TrikTextualInterpreter::abort()
{
	//mScriptRunner.abort();
	mBrick.stopWaiting();
	Q_ASSERT(mScriptRunner.thread() == thread());
	QMetaObject::invokeMethod(&mScriptRunner, "abort", Qt::QueuedConnection); // just a wild test
	mRunning = false; // reset brick?
	mBrick.processSensors(false);
}

void trik::TrikTextualInterpreter::init()
{
	mBrick.init(); // very crucial. Maybe move into interpret methods?
}

void trik::TrikTextualInterpreter::setErrorReporter(qReal::ErrorReporterInterface &errorReporter)
{
	mErrorReporter = &errorReporter;
}

bool trik::TrikTextualInterpreter::isRunning() const
{
	return mRunning;
}

void trik::TrikTextualInterpreter::reportError(const QString &msg)
{
	mErrorReporter->addError(msg);
	//	mBrick.abort(); what if there are more errors?
}

void trik::TrikTextualInterpreter::reportWarning(const QString &msg)
{
	mErrorReporter->addWarning(msg);
}

void trik::TrikTextualInterpreter::reportLog(const QString &msg)
{
	mErrorReporter->addInformation("log: " + msg);
}

QString trik::TrikTextualInterpreter::initInputs(const QString &inputs) const
{
	auto jsValToStr = [this](const QJsonValue &val) -> QString {
		switch (val.type()) {
		case QJsonValue::Bool:
			return val.toBool() ? "true" : "false";
		case QJsonValue::Double:
			return QString::number(val.Double);//maybe increase precision
		case QJsonValue::String:
			return QString("\"%1\"").arg(val.toString());
		case QJsonValue::Array:
			return QString(QJsonDocument(val.toArray()).toJson(QJsonDocument::Compact));
		case QJsonValue::Object:
			return QString(QJsonDocument(val.toObject()).toJson(QJsonDocument::Compact));
		default:
			mErrorReporter->addError(QObject::tr("Bogus input values"));
			return "";
		}
	};

	QString result;
	if (!inputs.isEmpty()) {
		QString val;
		QFile file;
		file.setFileName(inputs);
		if (file.open(QIODevice::ReadOnly)) {
			val = file.readAll();
			file.close();
			QJsonDocument document = QJsonDocument::fromJson(val.toUtf8());
			QJsonObject object = document.object();
			for (const QString &name : object.keys()) {
				QJsonValue value = object[name];
				QString valueStr = jsValToStr(value);
				QString line("var " + name + " = " + valueStr + ";\n");
				result.append(line);
			}
		} else {
			mErrorReporter->addError(QObject::tr("Error: File %1 couldn't be opened!").arg(inputs));
		}
	}
	return result;
}

void trik::TrikTextualInterpreter::setRunning(bool running)
{
	mRunning = running;
	mBrick.processSensors(running);
}

void trik::TrikTextualInterpreter::setCurrentDir(const QString &dir)
{
	mBrick.setCurrentDir(dir);
}

QStringList trik::TrikTextualInterpreter::supportedRobotModelNames() const
{
	return {"TwoDRobotModelForTrikV62RealRobotModel", "TwoDRobotModelForTrikV6RealRobotModel"};
}

QStringList trik::TrikTextualInterpreter::knownMethodNames() const
{
	return mScriptRunner.knownMethodNames();
}

void trik::TrikTextualInterpreter::reinitRobotsParts()
{
	mBrick.reinitImitationCamera();
}

void trik::TrikTextualInterpreter::scriptFinished(const QString &error, int scriptId)
{
	Q_UNUSED(scriptId);
	if (!error.isEmpty()) {
		reportError(error);
	}

	if (mRunning) { /// @todo: figure out better place for this check - it should avoid double aborts
		mRunning = false;
		mBrick.processSensors(false);
		emit completed();
	}
}
<|endoftext|>


<commit_before>#include "PDBHelper.h"
#include "../Misc/Trace.hpp"

#include <cstdint>
#include <sstream>

#define CHECK_SUCCESS(e)\
{ \
    HRESULT hresult = (e); \
    if(FAILED(hresult)) \
    { \
        BLACKBONE_TRACE( "PDB: %s: (%s) failed with HRESULT 0x%08x", __FUNCTION__, #e , hresult ); \
        return hresult; \
    } \
}

namespace blackbone
{

PDBHelper::PDBHelper()
{
    CoInitialize( nullptr );

    // If _NT_SYMBOL_PATH exists, use it
    wchar_t path[MAX_PATH] = { };
    if (GetEnvironmentVariableW( L"_NT_SYMBOL_PATH", path, _countof( path ) ) == 0)
    {
        if (GetTempPathW( _countof( path ), path ) != -1)
        {
            std::wstringstream wss;
            wss << L"srv*" << path << L"Symbols*https://msdl.microsoft.com/download/symbols";
            _sympath = wss.str();
        }
    }
}

PDBHelper::~PDBHelper()
{
    // Must be released before CoUninitialize
    _global.Release();
    _session.Release();
    _source.Release();

    CoUninitialize();
}

/// <summary>
/// Initialize symbols for target file
/// </summary>
/// <param name="file">Fully qualified path to target PE file</param>
/// <param name="base">Base ptr to add to RVAs</param>
/// <returns>Status code</returns>
HRESULT PDBHelper::Init( const std::wstring& file, ptr_t base /*= 0*/ )
{
    CHECK_SUCCESS( CoCreateDiaDataSource() );
    CHECK_SUCCESS( _source->loadDataForExe( file.c_str(), _sympath.empty() ? nullptr : _sympath.c_str(), nullptr ) );
    CHECK_SUCCESS( _source->openSession( &_session ) );
    CHECK_SUCCESS( _session->get_globalScope( &_global ) );

    _base = base;
    return PopulateSymbols();
}

/// <summary>
/// Get symbol RVA
/// </summary>
/// <param name="symName">Symbol name</param>
/// <param name="result">Resulting RVA</param>
/// <returns>Status code</returns>
HRESULT PDBHelper::GetSymAddress( const std::wstring& symName, ptr_t& result )
{
    auto found = _cache.find( symName );
    if (found != _cache.end())
    {
        result = _base + found->second;
        return S_OK;
    }

    return E_PDB_DBG_NOT_FOUND;
}

/// <summary>
/// Initialize DIA
/// </summary>
/// <returns>Status code</returns>
HRESULT PDBHelper::CoCreateDiaDataSource()
{
    const auto pSource = reinterpret_cast<void**>(&_source);

    // Try to get from COM
    HRESULT hr = CoCreateInstance( CLSID_DiaSource, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IDiaDataSource), pSource );

    // Retry with direct export call
    if (hr == REGDB_E_CLASSNOTREG)
    {
        HMODULE hMod = LoadLibraryW( L"msdia140.dll" );
        if (!hMod)
        {
            BLACKBONE_TRACE( "PDB: Failed to load msdia140.dll, error 0x08%x", GetLastError() );
            return HRESULT_FROM_WIN32( GetLastError() );
        }

        auto DllGetClassObject = reinterpret_cast<BOOL( WINAPI* )(REFCLSID, REFIID, LPVOID)>(GetProcAddress( hMod, "DllGetClassObject" ));
        if (!DllGetClassObject)
        {
            BLACKBONE_TRACE( "PDB: Failed to get DllGetClassObject from msdia140.dll" );
            return HRESULT_FROM_WIN32( GetLastError() );
        }

        CComPtr<IClassFactory> classFactory;
        CHECK_SUCCESS( DllGetClassObject( CLSID_DiaSource, IID_IClassFactory, &classFactory ) );
        hr = classFactory->CreateInstance( nullptr, IID_IDiaDataSource, pSource );
    }

    if (FAILED( hr ))
    {
        BLACKBONE_TRACE( "PDB: %s failed with HRESULT 0x08%x", __FUNCTION__, hr );
    }

    return hr;
}

/// <summary>
/// Build module symbol map
/// </summary>
/// <returns>Status code</returns>
HRESULT PDBHelper::PopulateSymbols()
{
    CComPtr<IDiaEnumSymbols> symbols;
    CHECK_SUCCESS( _global->findChildren( SymTagNull, nullptr, nsNone, &symbols ) );

    ULONG count = 0;
    CComPtr<IDiaSymbol> symbol;
    while (SUCCEEDED( symbols->Next( 1, &symbol, &count ) ) && count != 0)
    {
        DWORD rva = 0;
        BSTR name = nullptr;

        symbol->get_relativeVirtualAddress( &rva );
        symbol->get_undecoratedName( &name );

        if (name && rva)
        {
            std::wstring wname( name );

            // Remove x86 __stdcall decoration
            if (wname[0] == L'@' || wname[0] == L'_')
            {
                wname.erase( 0, 1 );
            }

            auto pos = wname.rfind( L'@' );
            if (pos != wname.npos)
            {
                wname.erase( pos );
            }

            _cache.emplace( std::move( wname ), rva );
        }

        symbol.Release();
    }

    return S_OK;
}

}<commit_msg>Symbol download fix<commit_after>#include "PDBHelper.h"
#include "../Misc/Trace.hpp"

#include <cstdint>
#include <sstream>

#define CHECK_SUCCESS(e)\
{ \
    HRESULT hresult = (e); \
    if(FAILED(hresult)) \
    { \
        BLACKBONE_TRACE( "PDB: %s: (%s) failed with HRESULT 0x%08x", __FUNCTION__, #e , hresult ); \
        return hresult; \
    } \
}

namespace blackbone
{

PDBHelper::PDBHelper()
{
    CoInitialize( nullptr );

    // If _NT_SYMBOL_PATH exists, use it
    wchar_t path[MAX_PATH] = { };
    if (GetEnvironmentVariableW( L"_NT_SYMBOL_PATH", path, _countof( path ) ) == 0)
    {
        if (GetTempPathW( _countof( path ), path ) != 0)
        {
            std::wstringstream wss;
            wss << L"srv*" << path << L"Symbols*http://msdl.microsoft.com/download/symbols";
            _sympath = wss.str();
        }
    }
}

PDBHelper::~PDBHelper()
{
    // Must be released before CoUninitialize
    _global.Release();
    _session.Release();
    _source.Release();

    CoUninitialize();
}

/// <summary>
/// Initialize symbols for target file
/// </summary>
/// <param name="file">Fully qualified path to target PE file</param>
/// <param name="base">Base ptr to add to RVAs</param>
/// <returns>Status code</returns>
HRESULT PDBHelper::Init( const std::wstring& file, ptr_t base /*= 0*/ )
{
    CHECK_SUCCESS( CoCreateDiaDataSource() );
    CHECK_SUCCESS( _source->loadDataForExe( file.c_str(), _sympath.empty() ? nullptr : _sympath.c_str(), nullptr ) );
    CHECK_SUCCESS( _source->openSession( &_session ) );
    CHECK_SUCCESS( _session->get_globalScope( &_global ) );

    _base = base;
    return PopulateSymbols();
}

/// <summary>
/// Get symbol RVA
/// </summary>
/// <param name="symName">Symbol name</param>
/// <param name="result">Resulting RVA</param>
/// <returns>Status code</returns>
HRESULT PDBHelper::GetSymAddress( const std::wstring& symName, ptr_t& result )
{
    auto found = _cache.find( symName );
    if (found != _cache.end())
    {
        result = _base + found->second;
        return S_OK;
    }

    return E_PDB_DBG_NOT_FOUND;
}

/// <summary>
/// Initialize DIA
/// </summary>
/// <returns>Status code</returns>
HRESULT PDBHelper::CoCreateDiaDataSource()
{
    const auto pSource = reinterpret_cast<void**>(&_source);

    // Try to get from COM
    HRESULT hr = CoCreateInstance( CLSID_DiaSource, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IDiaDataSource), pSource );

    // Retry with direct export call
    if (hr == REGDB_E_CLASSNOTREG)
    {
        HMODULE hMod = LoadLibraryW( L"msdia140.dll" );
        if (!hMod)
        {
            BLACKBONE_TRACE( "PDB: Failed to load msdia140.dll, error 0x08%x", GetLastError() );
            return HRESULT_FROM_WIN32( GetLastError() );
        }

        auto DllGetClassObject = reinterpret_cast<BOOL( WINAPI* )(REFCLSID, REFIID, LPVOID)>(GetProcAddress( hMod, "DllGetClassObject" ));
        if (!DllGetClassObject)
        {
            BLACKBONE_TRACE( "PDB: Failed to get DllGetClassObject from msdia140.dll" );
            return HRESULT_FROM_WIN32( GetLastError() );
        }

        CComPtr<IClassFactory> classFactory;
        CHECK_SUCCESS( DllGetClassObject( CLSID_DiaSource, IID_IClassFactory, &classFactory ) );
        hr = classFactory->CreateInstance( nullptr, IID_IDiaDataSource, pSource );
    }

    if (FAILED( hr ))
    {
        BLACKBONE_TRACE( "PDB: %s failed with HRESULT 0x08%x", __FUNCTION__, hr );
    }

    return hr;
}

/// <summary>
/// Build module symbol map
/// </summary>
/// <returns>Status code</returns>
HRESULT PDBHelper::PopulateSymbols()
{
    CComPtr<IDiaEnumSymbols> symbols;
    CHECK_SUCCESS( _global->findChildren( SymTagNull, nullptr, nsNone, &symbols ) );

    ULONG count = 0;
    CComPtr<IDiaSymbol> symbol;
    while (SUCCEEDED( symbols->Next( 1, &symbol, &count ) ) && count != 0)
    {
        DWORD rva = 0;
        BSTR name = nullptr;

        symbol->get_relativeVirtualAddress( &rva );
        symbol->get_undecoratedName( &name );

        if (name && rva)
        {
            std::wstring wname( name );

            // Remove x86 __stdcall decoration
            if (wname[0] == L'@' || wname[0] == L'_')
            {
                wname.erase( 0, 1 );
            }

            auto pos = wname.rfind( L'@' );
            if (pos != wname.npos)
            {
                wname.erase( pos );
            }

            _cache.emplace( std::move( wname ), rva );
        }

        symbol.Release();
    }

    return S_OK;
}

}<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015, Nils Asmussen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the FreeBSD Project.
 */

#include "debug/Dtu.hh"
#include "debug/DtuPf.hh"
#include "mem/dtu/dtu.hh"
#include "mem/dtu/pt_unit.hh"

const std::string
PtUnit::TranslateEvent::name() const
{
    return unit.dtu.name();
}

void
PtUnit::TranslateEvent::requestPTE()
{
    auto pkt = unit.createPacket(virt, ptAddr, level);
    if (!pkt)
    {
        finish(false, NocAddr(0));
        return;
    }

    unit.dtu.sendMemRequest(pkt,
                            reinterpret_cast<Addr>(this),
                            Dtu::MemReqType::TRANSLATION,
                            Cycles(0));
}

void
PtUnit::TranslateEvent::process()
{
    if (pf)
    {
        if (!unit.sendPagefaultMsg(this, virt, access))
            finish(false, NocAddr(0));
    }
    else
        requestPTE();
}

void
PtUnit::TranslateEvent::recvFromMem(PacketPtr pkt)
{
    Addr phys;
    uint flags = access;
    bool success = unit.finishTranslate(pkt, virt, level, &flags, &phys);

    if (success)
    {
        if (level > 0)
        {
            level--;
            ptAddr = phys;
            requestPTE();
            return;
        }

        Addr tlbVirt = virt & ~DtuTlb::PAGE_MASK;

        // we can't insert an entry twice
        NocAddr newPhys;
        if (unit.dtu.tlb->lookup(tlbVirt, access, &newPhys) != DtuTlb::HIT)
        {
            DPRINTFS(DtuPf, (&unit.dtu),
                "Inserting into TLB: virt=%p phys=%p flags=%u\n",
                tlbVirt, phys, flags);

            unit.dtu.tlb->insert(tlbVirt, NocAddr(phys), flags);
        }
        else
            assert(newPhys.getAddr() == phys);

        finish(success, NocAddr(phys + (virt & DtuTlb::PAGE_MASK)));
    }
    else
    {
        if (!unit.sendPagefaultMsg(this, virt, access))
            finish(false, NocAddr(0));
    }
}

bool
PtUnit::translateFunctional(Addr virt, uint access, NocAddr *phys)
{
    Addr ptePhys;
    Addr ptAddr = dtu.regs().get(DtuReg::ROOT_PT);
    for (int level = DtuTlb::LEVEL_CNT - 1; level >= 0; --level)
    {
        auto pkt = createPacket(virt, ptAddr, level);
        if (!pkt)
            return false;

        dtu.sendFunctionalMemRequest(pkt);

        if (!finishTranslate(pkt, virt, level, &access, &ptePhys))
            return false;

        ptAddr = ptePhys;
    }
    *phys = NocAddr(ptePhys + (virt & DtuTlb::PAGE_MASK));
    return true;
}

const char *
PtUnit::describeAccess(uint access)
{
    static char rwx[4];
    rwx[0] = (access & DtuTlb::READ ) ? 'r' : '-';
    rwx[1] = (access & DtuTlb::WRITE) ? 'w' : '-';
    rwx[2] = (access & DtuTlb::EXEC ) ? 'x' : '-';
    return rwx;
}

bool
PtUnit::sendPagefaultMsg(TranslateEvent *ev, Addr virt, uint access)
{
    if (~dtu.regs().get(DtuReg::STATUS) & static_cast<int>(Status::PAGEFAULTS))
    {
        DPRINTFS(DtuPf, (&dtu),
            "Pagefault (%s @ %p), but pagefault sending is disabled\n",
            describeAccess(access), virt);

        if (!pfqueue.empty())
        {
            DPRINTFS(DtuPf, (&dtu),
                "Dropping all pending pagefaults (%lu)\n",
                pfqueue.size());

            for (auto qev = pfqueue.begin(); qev != pfqueue.end(); ++qev)
                (*qev)->finish(false, NocAddr(0));
            pfqueue.clear();
        }
        return false;
    }

    int pfep = ev->toKernel ? Dtu::SYSCALL_EP : dtu.regs().get(DtuReg::PF_EP);
    assert(pfep < dtu.numEndpoints);
    SendEp ep = dtu.regs().getSendEp(pfep);

    // fall back to syscall EP, if the PF ep is invalid
    if(ep.maxMsgSize == 0)
    {
        ev->toKernel = true;
        pfep = Dtu::SYSCALL_EP;
        ep = dtu.regs().getSendEp(pfep);
    }

    size_t size = sizeof(Dtu::MessageHeader) + sizeof(PagefaultMessage);
    assert(size <= ep.maxMsgSize);

    if (pfqueue.empty())
        pfqueue.push_back(ev);
    else if (pfqueue.front() != ev)
    {
        uint page = virt >> DtuTlb::PAGE_BITS;
        for(auto qev = pfqueue.begin(); qev != pfqueue.end(); ++qev)
        {
            // can we merge the requests?
            if (access == (*qev)->access &&
                page == ((*qev)->virt >> DtuTlb::PAGE_BITS))
            {
                DPRINTFS(DtuPf, (&dtu),
                    "Adding Pagefault @ %#x to running request (%s @ %#x)\n",
                    virt, describeAccess(access), (*qev)->virt);

                (*qev)->trans.push_back(*ev->trans.begin());
                delete ev;
                return true;
            }
        }

        // try again later
        DPRINTFS(DtuPf, (&dtu),
            "Appending Pagefault (%s @ %#x) to queue\n",
            describeAccess(access), virt);

        ev->pf = true;
        pfqueue.push_back(ev);
        return true;
    }

    // create packet
    NocAddr nocAddr = NocAddr(ep.targetCore, ep.vpeId, ep.targetEp);
    auto pkt = dtu.generateRequest(nocAddr.getAddr(),
                                   size,
                                   MemCmd::WriteReq);

    // build the message and put it in the packet
    Dtu::MessageHeader header;
    header.length = sizeof(PagefaultMessage);
    header.flags = Dtu::PAGEFAULT | Dtu::REPLY_ENABLED;
    header.label = ep.label;
    header.senderEpId = pfep;
    header.senderCoreId = dtu.coreId;
    header.replyLabel = reinterpret_cast<uint64_t>(ev);
    // not used
    header.replyEpId = 0;

    memcpy(pkt->getPtr<uint8_t>(),
           &header,
           sizeof(header));

    PagefaultMessage msg;
    msg.opcode = PagefaultMessage::OPCODE_PF;
    msg.virt = virt;
    msg.access = access;

    memcpy(pkt->getPtr<uint8_t>() + sizeof(header),
           &msg,
           sizeof(msg));

    DPRINTFS(Dtu, (&dtu),
             "\e[1m[sd -> %u]\e[0m with EP%u for Pagefault (%s @ %p)\n",
             ep.targetCore, pfep, describeAccess(access), virt);

    DPRINTFS(Dtu, (&dtu),
        "  header: flags=%#x tgtEP=%u lbl=%#018lx rpLbl=%#018lx rpEP=%u\n",
        header.flags, ep.targetEp, header.label,
        header.replyLabel, header.replyEpId);

    // send the packet
    Cycles delay = dtu.transferToNocLatency;
    // delay += dtu.ticksToCycles(headerDelay);
    // pkt->payloadDelay = payloadDelay;
    dtu.printPacket(pkt);
    dtu.sendNocRequest(Dtu::NocPacketType::PAGEFAULT, pkt, delay);
    return true;
}

void
PtUnit::sendingPfFailed(PacketPtr pkt, int error)
{
    Dtu::MessageHeader* header = pkt->getPtr<Dtu::MessageHeader>();
    TranslateEvent *ev = reinterpret_cast<TranslateEvent*>(header->replyLabel);

    DPRINTFS(DtuPf, (&dtu),
        "Sending Pagefault (%s @ %p) failed (%d); notifying kernel\n",
        describeAccess(ev->access), ev->virt, error);

    if (error == Dtu::VPE_GONE)
    {
        ev->pf = true;
        ev->toKernel = true;
        dtu.schedule(ev, dtu.clockEdge(Cycles(1)));
    }
    else
    {
        panic("Unable to resolve pagefault (%s @ %p)",
            describeAccess(ev->access), ev->virt);
    }

    nextPagefault(ev);
}

void
PtUnit::finishPagefault(PacketPtr pkt)
{
    Dtu::MessageHeader* header = pkt->getPtr<Dtu::MessageHeader>();
    uint64_t *errorPtr = reinterpret_cast<uint64_t*>(header + 1);
    size_t expSize = sizeof(Dtu::MessageHeader) + sizeof(uint64_t);
    int error = pkt->getSize() == expSize ? *errorPtr : -1;

    TranslateEvent *ev = reinterpret_cast<TranslateEvent*>(header->label);

    DPRINTFS(Dtu, (&dtu),
        "\e[1m[rv <- %u]\e[0m %lu bytes for Pagefault (%s @ %p)\n",
        header->senderCoreId, header->length,
        describeAccess(ev->access), ev->virt);
    dtu.printPacket(pkt);

    pkt->makeResponse();

    nextPagefault(ev);

    if (!dtu.atomicMode)
    {
        Cycles delay = dtu.ticksToCycles(
            pkt->headerDelay + pkt->payloadDelay);
        delay += dtu.nocToTransferLatency;

        pkt->headerDelay = 0;
        pkt->payloadDelay = 0;

        dtu.schedNocRequestFinished(dtu.clockEdge(Cycles(1)));
        dtu.schedNocResponse(pkt, dtu.clockEdge(delay));
    }

    if (error != 0)
    {
        if (pkt->getSize() != expSize)
            DPRINTFS(DtuPf, (&dtu), "Invalid response for pagefault\n");
        else
        {
            DPRINTFS(DtuPf, (&dtu),
                "Pagefault for %s @ %p could not been resolved: %d\n",
                describeAccess(ev->access), ev->virt, error);
        }

        ev->finish(false, NocAddr(0));
        return;
    }

    DPRINTFS(DtuPf, (&dtu),
        "Retrying pagetable walk for %s @ %p\n",
        describeAccess(ev->access), ev->virt);

    // retry the translation
    ev->pf = false;
    ev->toKernel = false;
    dtu.schedule(ev, dtu.clockEdge(Cycles(1)));
}

void
PtUnit::nextPagefault(TranslateEvent *ev)
{
    assert(pfqueue.front() == ev);
    pfqueue.pop_front();

    if (!pfqueue.empty())
    {
        TranslateEvent *ev = pfqueue.front();
        dtu.schedule(ev, dtu.clockEdge(Cycles(1)));
    }
}

PacketPtr
PtUnit::createPacket(Addr virt, Addr ptAddr, int level)
{
    Addr idx = virt >> (DtuTlb::PAGE_BITS + level * DtuTlb::LEVEL_BITS);
    idx &= DtuTlb::LEVEL_MASK;

    NocAddr pteAddr(ptAddr + (idx << DtuTlb::PTE_BITS));
    auto pkt = dtu.generateRequest(pteAddr.getAddr(),
                                   sizeof(PtUnit::PageTableEntry),
                                   MemCmd::ReadReq);

    DPRINTFS(DtuPf, (&dtu), "Loading level %d PTE for %p from %p\n",
             level, virt, pteAddr.getAddr());

    return pkt;
}

bool
PtUnit::finishTranslate(PacketPtr pkt,
                        Addr virt,
                        int level,
                        uint *access,
                        Addr *phys)
{
    PageTableEntry *e = pkt->getPtr<PageTableEntry>();

    DPRINTFS(DtuPf, (&dtu), "Received level %d PTE for %p: %#x\n",
             level, virt, (uint64_t)*e);

    // for last-level PTEs, we need the desired access permissions
    if (level == 0)
    {
        if ((e->ixwr & *access) != *access)
            return false;
    }
    // for others, we don't need the INTERN bit
    else
    {
        uint pteAccess = *access & ~DtuTlb::INTERN;
        if ((e->ixwr & pteAccess) != pteAccess)
            return false;
    }

    *access = static_cast<uint>((uint64_t)e->ixwr);
    *phys = e->base << DtuTlb::PAGE_BITS;
    return true;
}

void
PtUnit::startTranslate(Addr virt, uint access, Translation *trans, bool pf)
{
    TranslateEvent *event = new TranslateEvent(*this);
    event->level = DtuTlb::LEVEL_CNT - 1;
    event->virt = virt;
    event->access = access;
    event->trans.push_back(trans);
    event->ptAddr = dtu.regs().get(DtuReg::ROOT_PT);
    event->pf = pf;
    event->toKernel = false;

    dtu.schedule(event, dtu.clockEdge(Cycles(1)));
}
<commit_msg>DTU: fixed bug in functional translate.<commit_after>/*
 * Copyright (c) 2015, Nils Asmussen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the FreeBSD Project.
 */

#include "debug/Dtu.hh"
#include "debug/DtuPf.hh"
#include "mem/dtu/dtu.hh"
#include "mem/dtu/pt_unit.hh"

const std::string
PtUnit::TranslateEvent::name() const
{
    return unit.dtu.name();
}

void
PtUnit::TranslateEvent::requestPTE()
{
    auto pkt = unit.createPacket(virt, ptAddr, level);
    if (!pkt)
    {
        finish(false, NocAddr(0));
        return;
    }

    unit.dtu.sendMemRequest(pkt,
                            reinterpret_cast<Addr>(this),
                            Dtu::MemReqType::TRANSLATION,
                            Cycles(0));
}

void
PtUnit::TranslateEvent::process()
{
    if (pf)
    {
        if (!unit.sendPagefaultMsg(this, virt, access))
            finish(false, NocAddr(0));
    }
    else
        requestPTE();
}

void
PtUnit::TranslateEvent::recvFromMem(PacketPtr pkt)
{
    Addr phys;
    uint flags = access;
    bool success = unit.finishTranslate(pkt, virt, level, &flags, &phys);

    if (success)
    {
        if (level > 0)
        {
            level--;
            ptAddr = phys;
            requestPTE();
            return;
        }

        Addr tlbVirt = virt & ~DtuTlb::PAGE_MASK;

        // we can't insert an entry twice
        NocAddr newPhys;
        if (unit.dtu.tlb->lookup(tlbVirt, access, &newPhys) != DtuTlb::HIT)
        {
            DPRINTFS(DtuPf, (&unit.dtu),
                "Inserting into TLB: virt=%p phys=%p flags=%u\n",
                tlbVirt, phys, flags);

            unit.dtu.tlb->insert(tlbVirt, NocAddr(phys), flags);
        }
        else
            assert(newPhys.getAddr() == phys);

        finish(success, NocAddr(phys + (virt & DtuTlb::PAGE_MASK)));
    }
    else
    {
        if (!unit.sendPagefaultMsg(this, virt, access))
            finish(false, NocAddr(0));
    }
}

bool
PtUnit::translateFunctional(Addr virt, uint access, NocAddr *phys)
{
    Addr ptePhys;
    Addr ptAddr = dtu.regs().get(DtuReg::ROOT_PT);
    for (int level = DtuTlb::LEVEL_CNT - 1; level >= 0; --level)
    {
        auto pkt = createPacket(virt, ptAddr, level);
        if (!pkt)
            return false;

        dtu.sendFunctionalMemRequest(pkt);

        uint acccopy = access;
        if (!finishTranslate(pkt, virt, level, &acccopy, &ptePhys))
            return false;

        ptAddr = ptePhys;
    }
    *phys = NocAddr(ptePhys + (virt & DtuTlb::PAGE_MASK));
    return true;
}

const char *
PtUnit::describeAccess(uint access)
{
    static char rwx[4];
    rwx[0] = (access & DtuTlb::READ ) ? 'r' : '-';
    rwx[1] = (access & DtuTlb::WRITE) ? 'w' : '-';
    rwx[2] = (access & DtuTlb::EXEC ) ? 'x' : '-';
    return rwx;
}

bool
PtUnit::sendPagefaultMsg(TranslateEvent *ev, Addr virt, uint access)
{
    if (~dtu.regs().get(DtuReg::STATUS) & static_cast<int>(Status::PAGEFAULTS))
    {
        DPRINTFS(DtuPf, (&dtu),
            "Pagefault (%s @ %p), but pagefault sending is disabled\n",
            describeAccess(access), virt);

        if (!pfqueue.empty())
        {
            DPRINTFS(DtuPf, (&dtu),
                "Dropping all pending pagefaults (%lu)\n",
                pfqueue.size());

            for (auto qev = pfqueue.begin(); qev != pfqueue.end(); ++qev)
                (*qev)->finish(false, NocAddr(0));
            pfqueue.clear();
        }
        return false;
    }

    int pfep = ev->toKernel ? Dtu::SYSCALL_EP : dtu.regs().get(DtuReg::PF_EP);
    assert(pfep < dtu.numEndpoints);
    SendEp ep = dtu.regs().getSendEp(pfep);

    // fall back to syscall EP, if the PF ep is invalid
    if(ep.maxMsgSize == 0)
    {
        ev->toKernel = true;
        pfep = Dtu::SYSCALL_EP;
        ep = dtu.regs().getSendEp(pfep);
    }

    size_t size = sizeof(Dtu::MessageHeader) + sizeof(PagefaultMessage);
    assert(size <= ep.maxMsgSize);

    if (pfqueue.empty())
        pfqueue.push_back(ev);
    else if (pfqueue.front() != ev)
    {
        uint page = virt >> DtuTlb::PAGE_BITS;
        for(auto qev = pfqueue.begin(); qev != pfqueue.end(); ++qev)
        {
            // can we merge the requests?
            if (access == (*qev)->access &&
                page == ((*qev)->virt >> DtuTlb::PAGE_BITS))
            {
                DPRINTFS(DtuPf, (&dtu),
                    "Adding Pagefault @ %#x to running request (%s @ %#x)\n",
                    virt, describeAccess(access), (*qev)->virt);

                (*qev)->trans.push_back(*ev->trans.begin());
                delete ev;
                return true;
            }
        }

        // try again later
        DPRINTFS(DtuPf, (&dtu),
            "Appending Pagefault (%s @ %#x) to queue\n",
            describeAccess(access), virt);

        ev->pf = true;
        pfqueue.push_back(ev);
        return true;
    }

    // create packet
    NocAddr nocAddr = NocAddr(ep.targetCore, ep.vpeId, ep.targetEp);
    auto pkt = dtu.generateRequest(nocAddr.getAddr(),
                                   size,
                                   MemCmd::WriteReq);

    // build the message and put it in the packet
    Dtu::MessageHeader header;
    header.length = sizeof(PagefaultMessage);
    header.flags = Dtu::PAGEFAULT | Dtu::REPLY_ENABLED;
    header.label = ep.label;
    header.senderEpId = pfep;
    header.senderCoreId = dtu.coreId;
    header.replyLabel = reinterpret_cast<uint64_t>(ev);
    // not used
    header.replyEpId = 0;

    memcpy(pkt->getPtr<uint8_t>(),
           &header,
           sizeof(header));

    PagefaultMessage msg;
    msg.opcode = PagefaultMessage::OPCODE_PF;
    msg.virt = virt;
    msg.access = access;

    memcpy(pkt->getPtr<uint8_t>() + sizeof(header),
           &msg,
           sizeof(msg));

    DPRINTFS(Dtu, (&dtu),
             "\e[1m[sd -> %u]\e[0m with EP%u for Pagefault (%s @ %p)\n",
             ep.targetCore, pfep, describeAccess(access), virt);

    DPRINTFS(Dtu, (&dtu),
        "  header: flags=%#x tgtEP=%u lbl=%#018lx rpLbl=%#018lx rpEP=%u\n",
        header.flags, ep.targetEp, header.label,
        header.replyLabel, header.replyEpId);

    // send the packet
    Cycles delay = dtu.transferToNocLatency;
    // delay += dtu.ticksToCycles(headerDelay);
    // pkt->payloadDelay = payloadDelay;
    dtu.printPacket(pkt);
    dtu.sendNocRequest(Dtu::NocPacketType::PAGEFAULT, pkt, delay);
    return true;
}

void
PtUnit::sendingPfFailed(PacketPtr pkt, int error)
{
    Dtu::MessageHeader* header = pkt->getPtr<Dtu::MessageHeader>();
    TranslateEvent *ev = reinterpret_cast<TranslateEvent*>(header->replyLabel);

    DPRINTFS(DtuPf, (&dtu),
        "Sending Pagefault (%s @ %p) failed (%d); notifying kernel\n",
        describeAccess(ev->access), ev->virt, error);

    if (error == Dtu::VPE_GONE)
    {
        ev->pf = true;
        ev->toKernel = true;
        dtu.schedule(ev, dtu.clockEdge(Cycles(1)));
    }
    else
    {
        panic("Unable to resolve pagefault (%s @ %p)",
            describeAccess(ev->access), ev->virt);
    }

    nextPagefault(ev);
}

void
PtUnit::finishPagefault(PacketPtr pkt)
{
    Dtu::MessageHeader* header = pkt->getPtr<Dtu::MessageHeader>();
    uint64_t *errorPtr = reinterpret_cast<uint64_t*>(header + 1);
    size_t expSize = sizeof(Dtu::MessageHeader) + sizeof(uint64_t);
    int error = pkt->getSize() == expSize ? *errorPtr : -1;

    TranslateEvent *ev = reinterpret_cast<TranslateEvent*>(header->label);

    DPRINTFS(Dtu, (&dtu),
        "\e[1m[rv <- %u]\e[0m %lu bytes for Pagefault (%s @ %p)\n",
        header->senderCoreId, header->length,
        describeAccess(ev->access), ev->virt);
    dtu.printPacket(pkt);

    pkt->makeResponse();

    nextPagefault(ev);

    if (!dtu.atomicMode)
    {
        Cycles delay = dtu.ticksToCycles(
            pkt->headerDelay + pkt->payloadDelay);
        delay += dtu.nocToTransferLatency;

        pkt->headerDelay = 0;
        pkt->payloadDelay = 0;

        dtu.schedNocRequestFinished(dtu.clockEdge(Cycles(1)));
        dtu.schedNocResponse(pkt, dtu.clockEdge(delay));
    }

    if (error != 0)
    {
        if (pkt->getSize() != expSize)
            DPRINTFS(DtuPf, (&dtu), "Invalid response for pagefault\n");
        else
        {
            DPRINTFS(DtuPf, (&dtu),
                "Pagefault for %s @ %p could not been resolved: %d\n",
                describeAccess(ev->access), ev->virt, error);
        }

        ev->finish(false, NocAddr(0));
        return;
    }

    DPRINTFS(DtuPf, (&dtu),
        "Retrying pagetable walk for %s @ %p\n",
        describeAccess(ev->access), ev->virt);

    // retry the translation
    ev->pf = false;
    ev->toKernel = false;
    dtu.schedule(ev, dtu.clockEdge(Cycles(1)));
}

void
PtUnit::nextPagefault(TranslateEvent *ev)
{
    assert(pfqueue.front() == ev);
    pfqueue.pop_front();

    if (!pfqueue.empty())
    {
        TranslateEvent *ev = pfqueue.front();
        dtu.schedule(ev, dtu.clockEdge(Cycles(1)));
    }
}

PacketPtr
PtUnit::createPacket(Addr virt, Addr ptAddr, int level)
{
    Addr idx = virt >> (DtuTlb::PAGE_BITS + level * DtuTlb::LEVEL_BITS);
    idx &= DtuTlb::LEVEL_MASK;

    NocAddr pteAddr(ptAddr + (idx << DtuTlb::PTE_BITS));
    auto pkt = dtu.generateRequest(pteAddr.getAddr(),
                                   sizeof(PtUnit::PageTableEntry),
                                   MemCmd::ReadReq);

    DPRINTFS(DtuPf, (&dtu), "Loading level %d PTE for %p from %p\n",
             level, virt, pteAddr.getAddr());

    return pkt;
}

bool
PtUnit::finishTranslate(PacketPtr pkt,
                        Addr virt,
                        int level,
                        uint *access,
                        Addr *phys)
{
    PageTableEntry *e = pkt->getPtr<PageTableEntry>();

    DPRINTFS(DtuPf, (&dtu), "Received level %d PTE for %p: %#x\n",
             level, virt, (uint64_t)*e);

    // for last-level PTEs, we need the desired access permissions
    if (level == 0)
    {
        if ((e->ixwr & *access) != *access)
            return false;
    }
    // for others, we don't need the INTERN bit
    else
    {
        uint pteAccess = *access & ~DtuTlb::INTERN;
        if ((e->ixwr & pteAccess) != pteAccess)
            return false;
    }

    *access = static_cast<uint>((uint64_t)e->ixwr);
    *phys = e->base << DtuTlb::PAGE_BITS;
    return true;
}

void
PtUnit::startTranslate(Addr virt, uint access, Translation *trans, bool pf)
{
    TranslateEvent *event = new TranslateEvent(*this);
    event->level = DtuTlb::LEVEL_CNT - 1;
    event->virt = virt;
    event->access = access;
    event->trans.push_back(trans);
    event->ptAddr = dtu.regs().get(DtuReg::ROOT_PT);
    event->pf = pf;
    event->toKernel = false;

    dtu.schedule(event, dtu.clockEdge(Cycles(1)));
}
<|endoftext|>