<commit_before>// Jubatus: Online machine learning framework for distributed environment
// Copyright (C) 2013 Preferred Infrastructure and Nippon Telegraph and Telephone Corporation.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License version 2.1 as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#include "save_load.hpp"

#include <ctime>
#include <fstream>
#include <string>
#include <vector>
#include <glog/logging.h>

#include "jubatus/core/common/exception.hpp"
#include "jubatus/core/common/big_endian.hpp"
#include "jubatus/core/common/crc32.hpp"
#include "jubatus/core/framework/mixable.hpp"

using jubatus::core::common::write_big_endian;
using jubatus::core::common::read_big_endian;

namespace jubatus {
namespace server {
namespace framework {

namespace {

const char magic_number[8] = "jubatus";
const uint64_t format_version = 1;

uint32_t jubatus_version_major = -1;
uint32_t jubatus_version_minor = -1;
uint32_t jubatus_version_maintenance = -1;

// TODO(gintenlabo): remove sscanf
void init_versions() {
  if (jubatus_version_major == static_cast<uint32_t>(-1)) {
    int major, minor, maintenance;
    std::sscanf(JUBATUS_VERSION, "%d.%d.%d", &major, &minor, &maintenance);  // NOLINT
    jubatus_version_major = major;
    jubatus_version_minor = minor;
    jubatus_version_maintenance = maintenance;
  }
}

const uint64_t system_data_container_version = 1;

struct system_data_container {
  uint64_t version;
  time_t timestamp;
  std::string type;
  std::string id;
  std::string config;

  system_data_container()
      : version(), timestamp(), type(), id(), config() {
  }

  system_data_container(const server_base& server, const std::string& id_)
      : version(system_data_container_version),
        timestamp(std::time(NULL)),
        type(server.argv().type), id(id_),
        config(server.get_config()) {
  }

  MSGPACK_DEFINE(version, timestamp, type, id, config);
};

uint32_t calc_crc32(const char* header,  // header size is 28 (fixed)
    const char* system_data, uint64_t system_data_size,
    const char* user_data, uint64_t user_data_size) {
  uint32_t crc32 = core::common::calc_crc32(header, 28);
  crc32 = core::common::calc_crc32(&header[32], 16, crc32);
  crc32 = core::common::calc_crc32(system_data, system_data_size, crc32);
  crc32 = core::common::calc_crc32(user_data, user_data_size, crc32);
  return crc32;
}

}  // namespace

void save_server(std::ostream& os,
    const server_base& server, const std::string& id) {
  init_versions();

  msgpack::sbuffer system_data_buf;
  msgpack::pack(&system_data_buf, system_data_container(server, id));

  msgpack::sbuffer user_data_buf;
  {
    msgpack::packer<msgpack::sbuffer> packer(user_data_buf);
    packer.pack_array(2);

    uint64_t user_data_version = server.user_data_version();
    packer.pack(user_data_version);
    server.get_mixable_holder()->pack(packer);
  }

  char header_buf[48];
  std::memcpy(header_buf, magic_number, 8);
  write_big_endian(format_version, &header_buf[8]);
  write_big_endian(jubatus_version_major, &header_buf[16]);
  write_big_endian(jubatus_version_minor, &header_buf[20]);
  write_big_endian(jubatus_version_maintenance, &header_buf[24]);
  // write_big_endian(crc32, &header_buf[28]);  // skipped
  write_big_endian(system_data_buf.size(), &header_buf[32]);
  write_big_endian(user_data_buf.size(), &header_buf[40]);

  uint32_t crc32 = calc_crc32(header_buf,
      system_data_buf.data(), system_data_buf.size(),
      user_data_buf.data(), user_data_buf.size());
  write_big_endian(crc32, &header_buf[28]);

  os.write(header_buf, 48);
  os.write(system_data_buf.data(), system_data_buf.size());
  os.write(user_data_buf.data(), user_data_buf.size());
}

// TODO(gintenlabo): implement detailed exception message
void load_server(std::istream& is,
    server_base& server, const std::string& id) {
  init_versions();

  char header_buf[48];
  is.read(header_buf, 48);
  if (std::memcmp(header_buf, magic_number, 8) != 0) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("invalid format"));
  }
  if (read_big_endian<uint64_t>(&header_buf[8]) != format_version) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("invalid format version"));
  }
  if (read_big_endian<uint32_t>(&header_buf[16]) != jubatus_version_major) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("invalid jubatus version"));
  }
  if (read_big_endian<uint32_t>(&header_buf[20]) != jubatus_version_minor) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("invalid jubatus version"));
  }
  if (read_big_endian<uint32_t>(&header_buf[24])
      != jubatus_version_maintenance) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("invalid jubatus version"));
  }
  uint32_t crc32_expected = read_big_endian<uint32_t>(&header_buf[28]);
  uint64_t system_data_size = read_big_endian<uint64_t>(&header_buf[32]);
  uint64_t user_data_size = read_big_endian<uint64_t>(&header_buf[40]);

  std::vector<char> system_data_buf(system_data_size);
  is.read(&system_data_buf[0], system_data_size);

  std::vector<char> user_data_buf(user_data_size);
  is.read(&user_data_buf[0], user_data_size);

  uint32_t crc32_actual = calc_crc32(header_buf,
      &system_data_buf[0], system_data_size,
      &user_data_buf[0], user_data_size);
  if (crc32_actual != crc32_expected) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("invalid crc32 sum"));
  }

  system_data_container system_data_actual;
  {
    msgpack::unpacked unpacked;
    msgpack::unpack(&unpacked, &system_data_buf[0], system_data_size);
    unpacked.get().convert(&system_data_actual);
  }
  system_data_container system_data_expected(server, id);
  if (system_data_actual.version != system_data_expected.version) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("invalid system data version"));
  }
  if (system_data_actual.type != system_data_expected.type) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("server type mismatched"));
  }
  if (system_data_actual.id != system_data_expected.id) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("server id mismatched"));
  }
  if (system_data_actual.config != system_data_expected.config) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("server config mismatched"));
  }

  {
    msgpack::unpacked unpacked;
    msgpack::unpack(&unpacked, &user_data_buf[0], user_data_size);

    std::vector<msgpack::object> objs;
    unpacked.get().convert(&objs);
    if (objs.size() != 2) {
      throw JUBATUS_EXCEPTION(
          core::common::exception::runtime_error("invalid user container"));
    }

    uint64_t user_data_version_expected = server.user_data_version();
    uint64_t user_data_version_actual;
    objs[0].convert(&user_data_version_actual);
    if (user_data_version_actual != user_data_version_expected) {
      throw JUBATUS_EXCEPTION(
          core::common::exception::runtime_error(
            "user data version mismatched"));
    }

    server.get_mixable_holder()->unpack(objs[1]);
  }
}

}  // namespace framework
}  // namespace server
}  // namespace jubatus
<commit_msg>Fix load behavior when booted with -model_file option<commit_after>// Jubatus: Online machine learning framework for distributed environment
// Copyright (C) 2013 Preferred Infrastructure and Nippon Telegraph and Telephone Corporation.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License version 2.1 as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#include "save_load.hpp"

#include <ctime>
#include <fstream>
#include <string>
#include <vector>
#include <glog/logging.h>

#include "jubatus/core/common/exception.hpp"
#include "jubatus/core/common/big_endian.hpp"
#include "jubatus/core/common/crc32.hpp"
#include "jubatus/core/framework/mixable.hpp"

using jubatus::core::common::write_big_endian;
using jubatus::core::common::read_big_endian;

namespace jubatus {
namespace server {
namespace framework {

namespace {

const char magic_number[8] = "jubatus";
const uint64_t format_version = 1;

uint32_t jubatus_version_major = -1;
uint32_t jubatus_version_minor = -1;
uint32_t jubatus_version_maintenance = -1;

// TODO(gintenlabo): remove sscanf
void init_versions() {
  if (jubatus_version_major == static_cast<uint32_t>(-1)) {
    int major, minor, maintenance;
    std::sscanf(JUBATUS_VERSION, "%d.%d.%d", &major, &minor, &maintenance);  // NOLINT
    jubatus_version_major = major;
    jubatus_version_minor = minor;
    jubatus_version_maintenance = maintenance;
  }
}

const uint64_t system_data_container_version = 1;

struct system_data_container {
  uint64_t version;
  time_t timestamp;
  std::string type;
  std::string id;
  std::string config;

  system_data_container()
      : version(), timestamp(), type(), id(), config() {
  }

  system_data_container(const server_base& server, const std::string& id_)
      : version(system_data_container_version),
        timestamp(std::time(NULL)),
        type(server.argv().type), id(id_),
        config(server.get_config()) {
  }

  MSGPACK_DEFINE(version, timestamp, type, id, config);
};

uint32_t calc_crc32(const char* header,  // header size is 28 (fixed)
    const char* system_data, uint64_t system_data_size,
    const char* user_data, uint64_t user_data_size) {
  uint32_t crc32 = core::common::calc_crc32(header, 28);
  crc32 = core::common::calc_crc32(&header[32], 16, crc32);
  crc32 = core::common::calc_crc32(system_data, system_data_size, crc32);
  crc32 = core::common::calc_crc32(user_data, user_data_size, crc32);
  return crc32;
}

}  // namespace

void save_server(std::ostream& os,
    const server_base& server, const std::string& id) {
  init_versions();

  msgpack::sbuffer system_data_buf;
  msgpack::pack(&system_data_buf, system_data_container(server, id));

  msgpack::sbuffer user_data_buf;
  {
    msgpack::packer<msgpack::sbuffer> packer(user_data_buf);
    packer.pack_array(2);

    uint64_t user_data_version = server.user_data_version();
    packer.pack(user_data_version);
    server.get_mixable_holder()->pack(packer);
  }

  char header_buf[48];
  std::memcpy(header_buf, magic_number, 8);
  write_big_endian(format_version, &header_buf[8]);
  write_big_endian(jubatus_version_major, &header_buf[16]);
  write_big_endian(jubatus_version_minor, &header_buf[20]);
  write_big_endian(jubatus_version_maintenance, &header_buf[24]);
  // write_big_endian(crc32, &header_buf[28]);  // skipped
  write_big_endian(system_data_buf.size(), &header_buf[32]);
  write_big_endian(user_data_buf.size(), &header_buf[40]);

  uint32_t crc32 = calc_crc32(header_buf,
      system_data_buf.data(), system_data_buf.size(),
      user_data_buf.data(), user_data_buf.size());
  write_big_endian(crc32, &header_buf[28]);

  os.write(header_buf, 48);
  os.write(system_data_buf.data(), system_data_buf.size());
  os.write(user_data_buf.data(), user_data_buf.size());
}

// TODO(gintenlabo): implement detailed exception message
void load_server(std::istream& is,
    server_base& server, const std::string& id) {
  init_versions();

  char header_buf[48];
  is.read(header_buf, 48);
  if (std::memcmp(header_buf, magic_number, 8) != 0) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("invalid format"));
  }
  if (read_big_endian<uint64_t>(&header_buf[8]) != format_version) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("invalid format version"));
  }
  if (read_big_endian<uint32_t>(&header_buf[16]) != jubatus_version_major) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("invalid jubatus version"));
  }
  if (read_big_endian<uint32_t>(&header_buf[20]) != jubatus_version_minor) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("invalid jubatus version"));
  }
  if (read_big_endian<uint32_t>(&header_buf[24])
      != jubatus_version_maintenance) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("invalid jubatus version"));
  }
  uint32_t crc32_expected = read_big_endian<uint32_t>(&header_buf[28]);
  uint64_t system_data_size = read_big_endian<uint64_t>(&header_buf[32]);
  uint64_t user_data_size = read_big_endian<uint64_t>(&header_buf[40]);

  std::vector<char> system_data_buf(system_data_size);
  is.read(&system_data_buf[0], system_data_size);

  std::vector<char> user_data_buf(user_data_size);
  is.read(&user_data_buf[0], user_data_size);

  uint32_t crc32_actual = calc_crc32(header_buf,
      &system_data_buf[0], system_data_size,
      &user_data_buf[0], user_data_size);
  if (crc32_actual != crc32_expected) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("invalid crc32 sum"));
  }

  system_data_container system_data_actual;
  {
    msgpack::unpacked unpacked;
    msgpack::unpack(&unpacked, &system_data_buf[0], system_data_size);
    unpacked.get().convert(&system_data_actual);
  }
  system_data_container system_data_expected(server, id);
  if (system_data_actual.version != system_data_expected.version) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("invalid system data version"));
  }
  if (system_data_actual.type != system_data_expected.type) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("server type mismatched"));
  }
  if (id != "" && system_data_actual.id != system_data_expected.id) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("server id mismatched"));
  }
  if (system_data_actual.config != system_data_expected.config) {
    throw JUBATUS_EXCEPTION(
        core::common::exception::runtime_error("server config mismatched"));
  }

  {
    msgpack::unpacked unpacked;
    msgpack::unpack(&unpacked, &user_data_buf[0], user_data_size);

    std::vector<msgpack::object> objs;
    unpacked.get().convert(&objs);
    if (objs.size() != 2) {
      throw JUBATUS_EXCEPTION(
          core::common::exception::runtime_error("invalid user container"));
    }

    uint64_t user_data_version_expected = server.user_data_version();
    uint64_t user_data_version_actual;
    objs[0].convert(&user_data_version_actual);
    if (user_data_version_actual != user_data_version_expected) {
      throw JUBATUS_EXCEPTION(
          core::common::exception::runtime_error(
            "user data version mismatched"));
    }

    server.get_mixable_holder()->unpack(objs[1]);
  }
}

}  // namespace framework
}  // namespace server
}  // namespace jubatus
<|endoftext|>


<commit_before>/* Copyright (c) 2017-2018 Hans-Kristian Arntzen
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "application.hpp"
#include "application_events.hpp"
#include "vulkan.hpp"

using namespace std;
using namespace Vulkan;

namespace Granite
{

static bool vulkan_update_display_mode(unsigned *width, unsigned *height, const VkDisplayModePropertiesKHR *mode,
                                       unsigned desired_width, unsigned desired_height)
{
	unsigned visible_width = mode->parameters.visibleRegion.width;
	unsigned visible_height = mode->parameters.visibleRegion.height;

	if (!desired_width || !desired_height)
	{
		/* Strategy here is to pick something which is largest resolution. */
		unsigned area = visible_width * visible_height;
		if (area > (*width) * (*height))
		{
			*width = visible_width;
			*height = visible_height;
			return true;
		}
		else
			return false;
	}
	else
	{
		/* For particular resolutions, find the closest. */
		int delta_x = int(desired_width) - int(visible_width);
		int delta_y = int(desired_height) - int(visible_height);
		int old_delta_x = int(desired_width) - int(*width);
		int old_delta_y = int(desired_height) - int(*height);

		int dist = delta_x * delta_x + delta_y * delta_y;
		int old_dist = old_delta_x * old_delta_x + old_delta_y * old_delta_y;
		if (dist < old_dist)
		{
			*width = visible_width;
			*height = visible_height;
			return true;
		}
		else
			return false;
	}
}

struct WSIPlatformDisplay : Vulkan::WSIPlatform
{
public:
	WSIPlatformDisplay(unsigned width, unsigned height)
		: width(width), height(height)
	{
		if (!Context::init_loader(nullptr))
			throw runtime_error("Failed to initialize Vulkan loader.");

		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Stopped);
		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Paused);
		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Running);
	}

	~WSIPlatformDisplay()
	{
		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Paused);
		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Stopped);
	}

	bool alive(Vulkan::WSI &) override
	{
		return true;
	}

	void poll_input() override
	{
		get_input_tracker().dispatch_current_state(get_frame_timer().get_frame_time());
	}

	vector<const char *> get_instance_extensions() override
	{
#ifdef KHR_DISPLAY_ACQUIRE_XLIB
		return { "VK_KHR_surface", "VK_KHR_display", "VK_EXT_acquire_xlib_display" };
#else
		return { "VK_KHR_surface", "VK_KHR_display" };
#endif
	}

	VkSurfaceKHR create_surface(VkInstance instance, VkPhysicalDevice gpu) override
	{
		VkSurfaceKHR surface = VK_NULL_HANDLE;

		uint32_t display_count;
		vkGetPhysicalDeviceDisplayPropertiesKHR(gpu, &display_count, nullptr);
		vector<VkDisplayPropertiesKHR> displays(display_count);
		vkGetPhysicalDeviceDisplayPropertiesKHR(gpu, &display_count, displays.data());

		uint32_t plane_count;
		vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, nullptr);
		vector<VkDisplayPlanePropertiesKHR> planes(plane_count);
		vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, planes.data());

#ifdef KHR_DISPLAY_ACQUIRE_XLIB
		VkDisplayKHR best_display = VK_NULL_HANDLE;
#endif
		VkDisplayModeKHR best_mode = VK_NULL_HANDLE;
		uint32_t best_plane = UINT32_MAX;

		const char *desired_display = getenv("GRANITE_DISPLAY_NAME");

		unsigned actual_width = 0;
		unsigned actual_height = 0;
		VkDisplayPlaneAlphaFlagBitsKHR alpha_mode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;

		for (unsigned dpy = 0; dpy < display_count; dpy++)
		{
			VkDisplayKHR display = displays[dpy].display;
			best_mode = VK_NULL_HANDLE;
			best_plane = UINT32_MAX;

			if (desired_display && strstr(displays[dpy].displayName, desired_display) != displays[dpy].displayName)
				continue;

			uint32_t mode_count;
			vkGetDisplayModePropertiesKHR(gpu, display, &mode_count, nullptr);
			vector<VkDisplayModePropertiesKHR> modes(mode_count);
			vkGetDisplayModePropertiesKHR(gpu, display, &mode_count, modes.data());

			for (unsigned i = 0; i < mode_count; i++)
			{
				const VkDisplayModePropertiesKHR &mode = modes[i];
				if (vulkan_update_display_mode(&actual_width, &actual_height, &mode, 0, 0))
					best_mode = mode.displayMode;
			}

			if (best_mode == VK_NULL_HANDLE)
				continue;

			for (unsigned i = 0; i < plane_count; i++)
			{
				uint32_t supported_count = 0;
				VkDisplayPlaneCapabilitiesKHR plane_caps;
				vkGetDisplayPlaneSupportedDisplaysKHR(gpu, i, &supported_count, nullptr);

				if (!supported_count)
					continue;

				vector<VkDisplayKHR> supported(supported_count);
				vkGetDisplayPlaneSupportedDisplaysKHR(gpu, i, &supported_count, supported.data());

				unsigned j;
				for (j = 0; j < supported_count; j++)
				{
					if (supported[j] == display)
					{
						if (best_plane == UINT32_MAX)
							best_plane = j;
						break;
					}
				}

				if (j == supported_count)
					continue;

				if (planes[i].currentDisplay == VK_NULL_HANDLE || planes[i].currentDisplay == display)
					best_plane = j;
				else
					continue;

				vkGetDisplayPlaneCapabilitiesKHR(gpu, best_mode, i, &plane_caps);

				if (plane_caps.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR)
				{
					best_plane = j;
					alpha_mode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
#ifdef KHR_DISPLAY_ACQUIRE_XLIB
					best_display = display;
#endif
					goto out;
				}
			}
		}
out:

		if (best_mode == VK_NULL_HANDLE)
			return VK_NULL_HANDLE;
		if (best_plane == UINT32_MAX)
			return VK_NULL_HANDLE;

		VkDisplaySurfaceCreateInfoKHR create_info = { VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR };
		create_info.displayMode = best_mode;
		create_info.planeIndex = best_plane;
		create_info.planeStackIndex = planes[best_plane].currentStackIndex;
		create_info.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
		create_info.globalAlpha = 1.0f;
		create_info.alphaMode = alpha_mode;
		create_info.imageExtent.width = actual_width;
		create_info.imageExtent.height = actual_height;
		this->width = actual_width;
		this->height = actual_height;

#ifdef KHR_DISPLAY_ACQUIRE_XLIB
		dpy = XOpenDisplay(nullptr);
		if (dpy)
		{
			if (vkAcquireXlibDisplayEXT(gpu, dpy, best_display) != VK_SUCCESS)
				LOGE("Failed to acquire Xlib display. Surface creation may fail.\n");
		}
#endif

		if (vkCreateDisplayPlaneSurfaceKHR(instance, &create_info, NULL, &surface) != VK_SUCCESS)
			return VK_NULL_HANDLE;
		return surface;
	}

	uint32_t get_surface_width() override
	{
		return width;
	}

	uint32_t get_surface_height() override
	{
		return height;
	}

	void notify_resize(unsigned width, unsigned height)
	{
		resize = true;
		this->width = width;
		this->height = height;
	}

private:
	unsigned width = 0;
	unsigned height = 0;
#ifdef KHR_DISPLAY_ACQUIRE_XLIB
	Display *dpy = nullptr;
#endif
};

void application_dummy()
{
}
}

int main(int argc, char *argv[])
{
	auto app = unique_ptr<Granite::Application>(Granite::application_create(argc, argv));
	if (app)
	{
		if (!app->init_wsi(make_unique<Granite::WSIPlatformDisplay>(1280, 720)))
			return 1;

		while (app->poll())
			app->run_frame();
		return 0;
	}
	else
		return 1;
}
<commit_msg>Fix KHR_display build.<commit_after>/* Copyright (c) 2017-2018 Hans-Kristian Arntzen
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "application.hpp"
#include "application_events.hpp"
#include "vulkan.hpp"
#include <string.h>

using namespace std;
using namespace Vulkan;

namespace Granite
{

static bool vulkan_update_display_mode(unsigned *width, unsigned *height, const VkDisplayModePropertiesKHR *mode,
                                       unsigned desired_width, unsigned desired_height)
{
	unsigned visible_width = mode->parameters.visibleRegion.width;
	unsigned visible_height = mode->parameters.visibleRegion.height;

	if (!desired_width || !desired_height)
	{
		/* Strategy here is to pick something which is largest resolution. */
		unsigned area = visible_width * visible_height;
		if (area > (*width) * (*height))
		{
			*width = visible_width;
			*height = visible_height;
			return true;
		}
		else
			return false;
	}
	else
	{
		/* For particular resolutions, find the closest. */
		int delta_x = int(desired_width) - int(visible_width);
		int delta_y = int(desired_height) - int(visible_height);
		int old_delta_x = int(desired_width) - int(*width);
		int old_delta_y = int(desired_height) - int(*height);

		int dist = delta_x * delta_x + delta_y * delta_y;
		int old_dist = old_delta_x * old_delta_x + old_delta_y * old_delta_y;
		if (dist < old_dist)
		{
			*width = visible_width;
			*height = visible_height;
			return true;
		}
		else
			return false;
	}
}

struct WSIPlatformDisplay : Vulkan::WSIPlatform
{
public:
	WSIPlatformDisplay(unsigned width, unsigned height)
		: width(width), height(height)
	{
		if (!Context::init_loader(nullptr))
			throw runtime_error("Failed to initialize Vulkan loader.");

		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Stopped);
		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Paused);
		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Running);
	}

	~WSIPlatformDisplay()
	{
		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Paused);
		EventManager::get_global().dequeue_all_latched(ApplicationLifecycleEvent::get_type_id());
		EventManager::get_global().enqueue_latched<ApplicationLifecycleEvent>(ApplicationLifecycle::Stopped);
	}

	bool alive(Vulkan::WSI &) override
	{
		return true;
	}

	void poll_input() override
	{
		get_input_tracker().dispatch_current_state(get_frame_timer().get_frame_time());
	}

	vector<const char *> get_instance_extensions() override
	{
#ifdef KHR_DISPLAY_ACQUIRE_XLIB
		return { "VK_KHR_surface", "VK_KHR_display", "VK_EXT_acquire_xlib_display" };
#else
		return { "VK_KHR_surface", "VK_KHR_display" };
#endif
	}

	VkSurfaceKHR create_surface(VkInstance instance, VkPhysicalDevice gpu) override
	{
		VkSurfaceKHR surface = VK_NULL_HANDLE;

		uint32_t display_count;
		vkGetPhysicalDeviceDisplayPropertiesKHR(gpu, &display_count, nullptr);
		vector<VkDisplayPropertiesKHR> displays(display_count);
		vkGetPhysicalDeviceDisplayPropertiesKHR(gpu, &display_count, displays.data());

		uint32_t plane_count;
		vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, nullptr);
		vector<VkDisplayPlanePropertiesKHR> planes(plane_count);
		vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, planes.data());

#ifdef KHR_DISPLAY_ACQUIRE_XLIB
		VkDisplayKHR best_display = VK_NULL_HANDLE;
#endif
		VkDisplayModeKHR best_mode = VK_NULL_HANDLE;
		uint32_t best_plane = UINT32_MAX;

		const char *desired_display = getenv("GRANITE_DISPLAY_NAME");

		unsigned actual_width = 0;
		unsigned actual_height = 0;
		VkDisplayPlaneAlphaFlagBitsKHR alpha_mode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;

		for (unsigned dpy = 0; dpy < display_count; dpy++)
		{
			VkDisplayKHR display = displays[dpy].display;
			best_mode = VK_NULL_HANDLE;
			best_plane = UINT32_MAX;

			if (desired_display && strstr(displays[dpy].displayName, desired_display) != displays[dpy].displayName)
				continue;

			uint32_t mode_count;
			vkGetDisplayModePropertiesKHR(gpu, display, &mode_count, nullptr);
			vector<VkDisplayModePropertiesKHR> modes(mode_count);
			vkGetDisplayModePropertiesKHR(gpu, display, &mode_count, modes.data());

			for (unsigned i = 0; i < mode_count; i++)
			{
				const VkDisplayModePropertiesKHR &mode = modes[i];
				if (vulkan_update_display_mode(&actual_width, &actual_height, &mode, 0, 0))
					best_mode = mode.displayMode;
			}

			if (best_mode == VK_NULL_HANDLE)
				continue;

			for (unsigned i = 0; i < plane_count; i++)
			{
				uint32_t supported_count = 0;
				VkDisplayPlaneCapabilitiesKHR plane_caps;
				vkGetDisplayPlaneSupportedDisplaysKHR(gpu, i, &supported_count, nullptr);

				if (!supported_count)
					continue;

				vector<VkDisplayKHR> supported(supported_count);
				vkGetDisplayPlaneSupportedDisplaysKHR(gpu, i, &supported_count, supported.data());

				unsigned j;
				for (j = 0; j < supported_count; j++)
				{
					if (supported[j] == display)
					{
						if (best_plane == UINT32_MAX)
							best_plane = j;
						break;
					}
				}

				if (j == supported_count)
					continue;

				if (planes[i].currentDisplay == VK_NULL_HANDLE || planes[i].currentDisplay == display)
					best_plane = j;
				else
					continue;

				vkGetDisplayPlaneCapabilitiesKHR(gpu, best_mode, i, &plane_caps);

				if (plane_caps.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR)
				{
					best_plane = j;
					alpha_mode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
#ifdef KHR_DISPLAY_ACQUIRE_XLIB
					best_display = display;
#endif
					goto out;
				}
			}
		}
out:

		if (best_mode == VK_NULL_HANDLE)
			return VK_NULL_HANDLE;
		if (best_plane == UINT32_MAX)
			return VK_NULL_HANDLE;

		VkDisplaySurfaceCreateInfoKHR create_info = { VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR };
		create_info.displayMode = best_mode;
		create_info.planeIndex = best_plane;
		create_info.planeStackIndex = planes[best_plane].currentStackIndex;
		create_info.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
		create_info.globalAlpha = 1.0f;
		create_info.alphaMode = alpha_mode;
		create_info.imageExtent.width = actual_width;
		create_info.imageExtent.height = actual_height;
		this->width = actual_width;
		this->height = actual_height;

#ifdef KHR_DISPLAY_ACQUIRE_XLIB
		dpy = XOpenDisplay(nullptr);
		if (dpy)
		{
			if (vkAcquireXlibDisplayEXT(gpu, dpy, best_display) != VK_SUCCESS)
				LOGE("Failed to acquire Xlib display. Surface creation may fail.\n");
		}
#endif

		if (vkCreateDisplayPlaneSurfaceKHR(instance, &create_info, NULL, &surface) != VK_SUCCESS)
			return VK_NULL_HANDLE;
		return surface;
	}

	uint32_t get_surface_width() override
	{
		return width;
	}

	uint32_t get_surface_height() override
	{
		return height;
	}

	void notify_resize(unsigned width, unsigned height)
	{
		resize = true;
		this->width = width;
		this->height = height;
	}

private:
	unsigned width = 0;
	unsigned height = 0;
#ifdef KHR_DISPLAY_ACQUIRE_XLIB
	Display *dpy = nullptr;
#endif
};

void application_dummy()
{
}
}

int main(int argc, char *argv[])
{
	auto app = unique_ptr<Granite::Application>(Granite::application_create(argc, argv));
	if (app)
	{
		if (!app->init_wsi(make_unique<Granite::WSIPlatformDisplay>(1280, 720)))
			return 1;

		while (app->poll())
			app->run_frame();
		return 0;
	}
	else
		return 1;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-present ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <boost/test/unit_test.hpp>
#include <seastar/testing/test_case.hh>
#include <seastar/testing/thread_test_case.hh>
#include "test/boost/sstable_test.hh"
#include <seastar/core/thread.hh>
#include "sstables/sstables.hh"
#include "test/lib/mutation_source_test.hh"
#include "test/lib/sstable_utils.hh"
#include "row_cache.hh"
#include "test/lib/simple_schema.hh"
#include "partition_slice_builder.hh"
#include "test/lib/flat_mutation_reader_assertions.hh"

using namespace sstables;
using namespace std::chrono_literals;

static
mutation_source make_sstable_mutation_source(sstables::test_env& env, schema_ptr s, sstring dir, std::vector<mutation> mutations,
        sstable_writer_config cfg, sstables::sstable::version_types version, gc_clock::time_point query_time = gc_clock::now()) {
    return as_mutation_source(make_sstable(env, s, dir, std::move(mutations), cfg, version, query_time));
}

static void consume_all(flat_mutation_reader& rd) {
    while (auto mfopt = rd().get0()) {}
}

// It is assumed that src won't change.
static snapshot_source snapshot_source_from_snapshot(mutation_source src) {
    return snapshot_source([src = std::move(src)] {
        return src;
    });
}

static
void test_cache_population_with_range_tombstone_adjacent_to_population_range(populate_fn_ex populate) {
    simple_schema s;
    tests::reader_concurrency_semaphore_wrapper semaphore;
    auto cache_mt = make_lw_shared<memtable>(s.schema());

    auto pkey = s.make_pkey();

    // underlying should not be empty, otherwise cache will make the whole range continuous
    mutation m1(s.schema(), pkey);
    s.add_row(m1, s.make_ckey(0), "v1");
    s.add_row(m1, s.make_ckey(1), "v2");
    s.add_row(m1, s.make_ckey(2), "v3");
    s.delete_range(m1, s.make_ckey_range(2, 100));
    cache_mt->apply(m1);

    cache_tracker tracker;
    auto ms = populate(s.schema(), std::vector<mutation>({m1}), gc_clock::now());
    row_cache cache(s.schema(), snapshot_source_from_snapshot(std::move(ms)), tracker);

    auto pr = dht::partition_range::make_singular(pkey);

    auto populate_range = [&] (int start) {
        auto slice = partition_slice_builder(*s.schema())
                .with_range(query::clustering_range::make_singular(s.make_ckey(start)))
                .build();
        auto rd = cache.make_reader(s.schema(), semaphore.make_permit(), pr, slice);
        auto close_rd = deferred_close(rd);
        consume_all(rd);
    };

    populate_range(2);

    // The cache now has only row with ckey 2 populated and the rest is discontinuous.
    // Populating reader which stops populating at entry with ckey 2 should not forget
    // to emit range_tombstone which starts at before(2).

    assert_that(cache.make_reader(s.schema(), semaphore.make_permit()))
            .produces(m1)
            .produces_end_of_stream();
}

static future<> test_sstable_conforms_to_mutation_source(sstable_version_types version, int index_block_size) {
    return sstables::test_env::do_with_async([version, index_block_size] (sstables::test_env& env) {
                sstable_writer_config cfg = env.manager().configure_writer();
                cfg.promoted_index_block_size = index_block_size;

                std::vector<tmpdir> dirs;
                auto populate = [&env, &dirs, &cfg, version] (schema_ptr s, const std::vector<mutation>& partitions,
                                                              gc_clock::time_point query_time) -> mutation_source {
                    dirs.emplace_back();
                    return make_sstable_mutation_source(env, s, dirs.back().path().string(), partitions, cfg, version, query_time);
                };

                run_mutation_source_tests(populate);

                if (index_block_size == 1) {
                    // The tests below are not sensitive to index bock size so run once.
                    test_cache_population_with_range_tombstone_adjacent_to_population_range(populate);
                }
    });
}

static constexpr std::array<int, 3> block_sizes = { 1, 128, 64 * 1024 };

// Split for better parallelizm

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_mc_tiny) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[0], block_sizes[0]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_mc_medium) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[0], block_sizes[1]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_mc_large) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[0], block_sizes[2]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_md_tiny) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[1], block_sizes[0]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_md_medium) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[1], block_sizes[1]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_md_large) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[1], block_sizes[2]);
}

// This assert makes sure we don't miss writable vertions
static_assert(writable_sstable_versions.size() == 2);
<commit_msg>tests: Fix indentation after previous patch<commit_after>/*
 * Copyright (C) 2015-present ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <boost/test/unit_test.hpp>
#include <seastar/testing/test_case.hh>
#include <seastar/testing/thread_test_case.hh>
#include "test/boost/sstable_test.hh"
#include <seastar/core/thread.hh>
#include "sstables/sstables.hh"
#include "test/lib/mutation_source_test.hh"
#include "test/lib/sstable_utils.hh"
#include "row_cache.hh"
#include "test/lib/simple_schema.hh"
#include "partition_slice_builder.hh"
#include "test/lib/flat_mutation_reader_assertions.hh"

using namespace sstables;
using namespace std::chrono_literals;

static
mutation_source make_sstable_mutation_source(sstables::test_env& env, schema_ptr s, sstring dir, std::vector<mutation> mutations,
        sstable_writer_config cfg, sstables::sstable::version_types version, gc_clock::time_point query_time = gc_clock::now()) {
    return as_mutation_source(make_sstable(env, s, dir, std::move(mutations), cfg, version, query_time));
}

static void consume_all(flat_mutation_reader& rd) {
    while (auto mfopt = rd().get0()) {}
}

// It is assumed that src won't change.
static snapshot_source snapshot_source_from_snapshot(mutation_source src) {
    return snapshot_source([src = std::move(src)] {
        return src;
    });
}

static
void test_cache_population_with_range_tombstone_adjacent_to_population_range(populate_fn_ex populate) {
    simple_schema s;
    tests::reader_concurrency_semaphore_wrapper semaphore;
    auto cache_mt = make_lw_shared<memtable>(s.schema());

    auto pkey = s.make_pkey();

    // underlying should not be empty, otherwise cache will make the whole range continuous
    mutation m1(s.schema(), pkey);
    s.add_row(m1, s.make_ckey(0), "v1");
    s.add_row(m1, s.make_ckey(1), "v2");
    s.add_row(m1, s.make_ckey(2), "v3");
    s.delete_range(m1, s.make_ckey_range(2, 100));
    cache_mt->apply(m1);

    cache_tracker tracker;
    auto ms = populate(s.schema(), std::vector<mutation>({m1}), gc_clock::now());
    row_cache cache(s.schema(), snapshot_source_from_snapshot(std::move(ms)), tracker);

    auto pr = dht::partition_range::make_singular(pkey);

    auto populate_range = [&] (int start) {
        auto slice = partition_slice_builder(*s.schema())
                .with_range(query::clustering_range::make_singular(s.make_ckey(start)))
                .build();
        auto rd = cache.make_reader(s.schema(), semaphore.make_permit(), pr, slice);
        auto close_rd = deferred_close(rd);
        consume_all(rd);
    };

    populate_range(2);

    // The cache now has only row with ckey 2 populated and the rest is discontinuous.
    // Populating reader which stops populating at entry with ckey 2 should not forget
    // to emit range_tombstone which starts at before(2).

    assert_that(cache.make_reader(s.schema(), semaphore.make_permit()))
            .produces(m1)
            .produces_end_of_stream();
}

static future<> test_sstable_conforms_to_mutation_source(sstable_version_types version, int index_block_size) {
    return sstables::test_env::do_with_async([version, index_block_size] (sstables::test_env& env) {
        sstable_writer_config cfg = env.manager().configure_writer();
        cfg.promoted_index_block_size = index_block_size;

        std::vector<tmpdir> dirs;
        auto populate = [&env, &dirs, &cfg, version] (schema_ptr s, const std::vector<mutation>& partitions,
                                                      gc_clock::time_point query_time) -> mutation_source {
            dirs.emplace_back();
            return make_sstable_mutation_source(env, s, dirs.back().path().string(), partitions, cfg, version, query_time);
        };

        run_mutation_source_tests(populate);

        if (index_block_size == 1) {
            // The tests below are not sensitive to index bock size so run once.
            test_cache_population_with_range_tombstone_adjacent_to_population_range(populate);
        }
    });
}

static constexpr std::array<int, 3> block_sizes = { 1, 128, 64 * 1024 };

// Split for better parallelizm

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_mc_tiny) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[0], block_sizes[0]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_mc_medium) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[0], block_sizes[1]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_mc_large) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[0], block_sizes[2]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_md_tiny) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[1], block_sizes[0]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_md_medium) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[1], block_sizes[1]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_md_large) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[1], block_sizes[2]);
}

// This assert makes sure we don't miss writable vertions
static_assert(writable_sstable_versions.size() == 2);
<|endoftext|>


<commit_before><commit_msg>print stack trace to stderr as well as to dialog.  Stacktrace info was shown only in dialog, and chromeos was not getting this as it does not have xmessage. Changed to include stacktrace to the log message before being printed to stderr.<commit_after><|endoftext|>


<commit_before>/**
 * Copyright (c) 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

#include "RedundantMoveEliminationPass.h"

#include <boost/optional.hpp>

#include "AliasedRegisters.h"
#include "ControlFlow.h"
#include "DexUtil.h"
#include "FixpointIterators.h"
#include "IRInstruction.h"
#include "ParallelWalkers.h"
#include "PassManager.h"

/**
 * This pass eliminates writes to registers that already hold the written value.
 *
 * It's more commonly known as Copy Propagation.
 *
 * For example,
 *   move-object/from16 v0, v33
 *   iget-object v2, v0, LX/04b;.a:Landroid/content/Context; // field@05d6
 *   move-object/from16 v0, v33
 *   iget-object v3, v0, LX/04b;.b:Ljava/lang/String; // field@05d7
 *   move-object/from16 v0, v33
 *   iget-object v4, v0, LX/04b;.c:LX/04K; // field@05d8
 *   move-object/from16 v0, v33
 *
 * It keeps moving v33 to v0 even though they hold the same object!
 *
 * This optimization transforms the above code to this:
 *   move-object/from16 v0, v33
 *   iget-object v2, v0, LX/04b;.a:Landroid/content/Context; // field@05d6
 *   iget-object v3, v0, LX/04b;.b:Ljava/lang/String; // field@05d7
 *   iget-object v4, v0, LX/04b;.c:LX/04K; // field@05d8
 *
 * It does so by examinining all the writes to registers in a basic block, if vA
 * is moved into vB, then vA and vB are aliases until one of them is written
 * with a different value. Any move between registers that are already aliased
 * is unneccesary. Eliminate them.
 *
 * It can also do the same thing with constant loads, if enabled by the config.
 *
 * This optimization also will replace source registers with a representative
 * register (a whole alias group has a single representative). The reason is
 * that if we use fewer registers, DCE could clean up some more moves after
 * us.
 *
 * Possible additions: (TODO?)
 *   wide registers (I tried it. it's only a tiny help. --jhendrick)
 */

namespace {

struct RedundantStats {
  size_t moves_eliminated{0};
  size_t replaced_sources{0};

  RedundantStats() = default;
  RedundantStats(size_t elim, size_t replaced) : moves_eliminated(elim), replaced_sources(replaced) {}

  RedundantStats operator+(const RedundantStats& other);
};

RedundantStats RedundantStats::operator+(const RedundantStats& other) {
  return RedundantStats{moves_eliminated + other.moves_eliminated,
                        replaced_sources + other.replaced_sources};
}

class AliasFixpointIterator final
    : public MonotonicFixpointIterator<Block*, AliasDomain> {
 public:
  const RedundantMoveEliminationPass::Config& m_config;
  RedundantStats& m_stats;

  using BlockToListFunc = std::function<std::vector<Block*>(Block* const&)>;

  AliasFixpointIterator(Block* start_block,
                        BlockToListFunc succ,
                        BlockToListFunc pred,
                        const RedundantMoveEliminationPass::Config& config,
                        RedundantStats& stats)
      : MonotonicFixpointIterator<Block*, AliasDomain>(start_block, succ, pred),
        m_config(config),
        m_stats(stats) {}

  /*
   * if deletes is not null, this time is for real.
   * fill the `deletes` vector with redundant instructions
   *
   * if deletes is null, analyze only. make no changes.
   *
   * An instruction can be removed if we know the source and destination are
   * aliases
   */
  void run_on_block(Block* const& block,
                    AliasedRegisters& aliases,
                    std::vector<IRInstruction*>* deletes) const {

    for (auto& mei : InstructionIterable(block)) {
      const RegisterValue& src = get_src_value(mei.insn);
      if (src != RegisterValue::none()) {
        // either a move or a constant load into `dst`
        RegisterValue dst{mei.insn->dest()};
        if (aliases.are_aliases(dst, src)) {
          if (deletes != nullptr) {
            deletes->push_back(mei.insn);
          }
        } else {
          aliases.break_alias(dst);
          aliases.make_aliased(dst, src);
        }
      } else {
        if (m_config.replace_with_representative && deletes != nullptr) {
          replace_with_representative(mei.insn, aliases);
        }
        if (mei.insn->dests_size() > 0) {
          // dest is being written to but not by a simple move from another
          // register or a constant load. Break its aliases because we don't
          // know what its value is.
          RegisterValue dst{mei.insn->dest()};
          aliases.break_alias(dst);
          if (mei.insn->dest_is_wide()) {
            Register wide_reg = mei.insn->dest() + 1;
            RegisterValue wide{wide_reg};
            aliases.break_alias(wide);
          }
        }
      }
    }
  }

  // Each group of aliases has one representative register.
  // Try to replace source registers with their representative.
  void replace_with_representative(IRInstruction* insn,
                                   AliasedRegisters& aliases) const {
    if (insn->srcs_size() > 0 &&
        !opcode::has_range(insn->opcode()) && // range has to stay in order
        // we need to make sure the dest and src of check-cast stay identical,
        // because the dest is simply an alias to the src. See the comments in
        // IRInstruction.h for details.
        insn->opcode() != OPCODE_CHECK_CAST && !is_monitor(insn->opcode())) {
      for (size_t i = 0; i < insn->srcs_size(); ++i) {
        RegisterValue val{insn->src(i)};
        boost::optional<Register> rep = aliases.get_representative(val);
        if (rep) {
          // filter out uses of wide registers where the second register isn't
          // also aliased
          if (insn->src_is_wide(i)) {
            RegisterValue orig_wide{(Register)(insn->src(i) + 1)};
            RegisterValue rep_wide{(Register)(*rep + 1)};
            bool wides_are_aliased = aliases.are_aliases(orig_wide, rep_wide);
            if (!wides_are_aliased) {
              continue;
            }
          }

          if (insn->src(i) != *rep) {
            insn->set_src(i, *rep);
            m_stats.replaced_sources++;
          }
        }
      }
    }
  }

  const RegisterValue get_src_value(IRInstruction* insn) const {
    switch (insn->opcode()) {
    case OPCODE_MOVE:
    case OPCODE_MOVE_FROM16:
    case OPCODE_MOVE_16:
    case OPCODE_MOVE_OBJECT:
    case OPCODE_MOVE_OBJECT_FROM16:
    case OPCODE_MOVE_OBJECT_16:
      return RegisterValue{insn->src(0)};
    case OPCODE_CONST:
    case OPCODE_CONST_4:
    case OPCODE_CONST_16:
      if (m_config.eliminate_const_literals) {
        return RegisterValue{insn->literal()};
      } else {
        return RegisterValue::none();
      }
    case OPCODE_CONST_STRING:
    case OPCODE_CONST_STRING_JUMBO: {
      if (m_config.eliminate_const_strings) {
        DexString* str = insn->get_string();
        return RegisterValue{str};
      } else {
        return RegisterValue::none();
      }
    }
    case OPCODE_CONST_CLASS: {
      if (m_config.eliminate_const_classes) {
        DexType* type = insn->get_type();
        return RegisterValue{type};
      } else {
        return RegisterValue::none();
      }
    }
    default:
      return RegisterValue::none();
    }
  }

  void analyze_node(Block* const& node,
                    AliasDomain* current_state) const override {
    current_state->update([&](AliasedRegisters& aliases) {
      run_on_block(node, aliases, nullptr);
    });
  }

  AliasDomain analyze_edge(
      Block* const& /* source */,
      Block* const& /* target */,
      const AliasDomain& exit_state_at_source) const override {
    return exit_state_at_source;
  }
};

class RedundantMoveEliminationImpl final {
 public:
  explicit RedundantMoveEliminationImpl(
      const RedundantMoveEliminationPass::Config& config)
      : m_config(config) {}

  RedundantStats run(Scope scope) {
    using Data = std::nullptr_t;
    using Output = RedundantStats;
    return walk_methods_parallel<Data, Output>(
        scope,
        [this](Data&, DexMethod* m) {
          if (m->get_code()) {
            return run_on_method(m);
          }
          return RedundantStats();
        },
        [](Output a, Output b) { return a + b; },
        [](unsigned int /*thread_index*/) { return nullptr; });
  }

 private:
  const RedundantMoveEliminationPass::Config& m_config;

  RedundantStats run_on_method(DexMethod* method) {
    std::vector<IRInstruction*> deletes;
    RedundantStats stats;

    auto code = method->get_code();
    code->build_cfg();
    const auto& blocks = code->cfg().blocks();

    AliasFixpointIterator fixpoint(
        code->cfg().entry_block(),
        [](Block* const& block) { return block->succs(); },
        [](Block* const& block) { return block->preds(); },
        m_config,
        stats);

    if (m_config.full_method_analysis) {
      fixpoint.run(AliasDomain());
      for (auto block : blocks) {
        AliasDomain domain = fixpoint.get_entry_state_at(block);
        domain.update([&fixpoint, block, &deletes](AliasedRegisters& aliases) {
          fixpoint.run_on_block(block, aliases, &deletes);
        });
      }
    } else {
      for (auto block : blocks) {
        AliasedRegisters aliases;
        fixpoint.run_on_block(block, aliases, &deletes);
      }
    }

    stats.moves_eliminated += deletes.size();
    for (auto insn : deletes) {
      code->remove_opcode(insn);
    }
    return stats;
  }
};
}

void RedundantMoveEliminationPass::run_pass(DexStoresVector& stores,
                                            ConfigFiles& /* unused */,
                                            PassManager& mgr) {
  auto scope = build_class_scope(stores);

  if (m_config.eliminate_const_literals && !mgr.verify_none_enabled()) {
    // This option is not safe with the verifier
    m_config.eliminate_const_literals = false;
    TRACE(RME,
          1,
          "Ignoring eliminate_const_literals because verify-none is not "
          "enabled.\n");
  }

  RedundantMoveEliminationImpl impl(m_config);
  auto stats = impl.run(scope);
  mgr.incr_metric("redundant_moves_eliminated", stats.moves_eliminated);
  mgr.incr_metric("source_regs_replaced_with_representative",
                  stats.replaced_sources);
  TRACE(RME,
        1,
        "%d redundant moves eliminated\n",
        mgr.get_metric("redundant_moves_eliminated"));
  TRACE(RME,
        1,
        "%d source registers replaced with representative\n",
        mgr.get_metric("source_regs_replaced_with_representative"));
}

static RedundantMoveEliminationPass s_pass;
<commit_msg>RME: Adapt to move-result-pseudo instructions [3/N]<commit_after>/**
 * Copyright (c) 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

#include "RedundantMoveEliminationPass.h"

#include <boost/optional.hpp>

#include "AliasedRegisters.h"
#include "ControlFlow.h"
#include "DexUtil.h"
#include "FixpointIterators.h"
#include "IRInstruction.h"
#include "ParallelWalkers.h"
#include "PassManager.h"

/**
 * This pass eliminates writes to registers that already hold the written value.
 *
 * It's more commonly known as Copy Propagation.
 *
 * For example,
 *   move-object/from16 v0, v33
 *   iget-object v2, v0, LX/04b;.a:Landroid/content/Context; // field@05d6
 *   move-object/from16 v0, v33
 *   iget-object v3, v0, LX/04b;.b:Ljava/lang/String; // field@05d7
 *   move-object/from16 v0, v33
 *   iget-object v4, v0, LX/04b;.c:LX/04K; // field@05d8
 *   move-object/from16 v0, v33
 *
 * It keeps moving v33 to v0 even though they hold the same object!
 *
 * This optimization transforms the above code to this:
 *   move-object/from16 v0, v33
 *   iget-object v2, v0, LX/04b;.a:Landroid/content/Context; // field@05d6
 *   iget-object v3, v0, LX/04b;.b:Ljava/lang/String; // field@05d7
 *   iget-object v4, v0, LX/04b;.c:LX/04K; // field@05d8
 *
 * It does so by examinining all the writes to registers in a basic block, if vA
 * is moved into vB, then vA and vB are aliases until one of them is written
 * with a different value. Any move between registers that are already aliased
 * is unneccesary. Eliminate them.
 *
 * It can also do the same thing with constant loads, if enabled by the config.
 *
 * This optimization also will replace source registers with a representative
 * register (a whole alias group has a single representative). The reason is
 * that if we use fewer registers, DCE could clean up some more moves after
 * us.
 *
 * Possible additions: (TODO?)
 *   wide registers (I tried it. it's only a tiny help. --jhendrick)
 */

namespace {

struct RedundantStats {
  size_t moves_eliminated{0};
  size_t replaced_sources{0};

  RedundantStats() = default;
  RedundantStats(size_t elim, size_t replaced) : moves_eliminated(elim), replaced_sources(replaced) {}

  RedundantStats operator+(const RedundantStats& other);
};

RedundantStats RedundantStats::operator+(const RedundantStats& other) {
  return RedundantStats{moves_eliminated + other.moves_eliminated,
                        replaced_sources + other.replaced_sources};
}

class AliasFixpointIterator final
    : public MonotonicFixpointIterator<Block*, AliasDomain> {
 public:
  const RedundantMoveEliminationPass::Config& m_config;
  RedundantStats& m_stats;

  using BlockToListFunc = std::function<std::vector<Block*>(Block* const&)>;

  AliasFixpointIterator(Block* start_block,
                        BlockToListFunc succ,
                        BlockToListFunc pred,
                        const RedundantMoveEliminationPass::Config& config,
                        RedundantStats& stats)
      : MonotonicFixpointIterator<Block*, AliasDomain>(start_block, succ, pred),
        m_config(config),
        m_stats(stats) {}

  /*
   * if deletes is not null, this time is for real.
   * fill the `deletes` vector with redundant instructions
   *
   * if deletes is null, analyze only. make no changes.
   *
   * An instruction can be removed if we know the source and destination are
   * aliases
   */
  void run_on_block(Block* block,
                    AliasedRegisters& aliases,
                    std::vector<IRInstruction*>* deletes) const {

    for (auto it = block->begin(); it != block->end(); ++it) {
      if (it->type != MFLOW_OPCODE) {
        continue;
      }
      auto insn = it->insn;
      auto op = insn->opcode();
      RegisterValue src;
      if (opcode::is_move_result_pseudo(op)) {
        src = get_src_value(primary_instruction_of_move_result_pseudo(it));
      } else if (!insn->has_move_result_pseudo()) {
        src = get_src_value(insn);
      }
      if (src != RegisterValue::none()) {
        // either a move or a constant load into `dst`
        RegisterValue dst = RegisterValue{insn->dest()};
        if (aliases.are_aliases(dst, src)) {
          if (deletes != nullptr) {
            if (opcode::is_move_result_pseudo(op)) {
              deletes->push_back(primary_instruction_of_move_result_pseudo(it));
            } else {
              deletes->push_back(insn);
            }
          }
        } else {
          aliases.break_alias(dst);
          aliases.make_aliased(dst, src);
        }
      } else {
        if (m_config.replace_with_representative && deletes != nullptr) {
          replace_with_representative(insn, aliases);
        }
        if (insn->dests_size() > 0) {
          // dest is being written to but not by a simple move from another
          // register or a constant load. Break its aliases because we don't
          // know what its value is.
          RegisterValue dst{insn->dest()};
          aliases.break_alias(dst);
          if (insn->dest_is_wide()) {
            Register wide_reg = insn->dest() + 1;
            RegisterValue wide{wide_reg};
            aliases.break_alias(wide);
          }
        }
      }
    }
  }

  // Each group of aliases has one representative register.
  // Try to replace source registers with their representative.
  void replace_with_representative(IRInstruction* insn,
                                   AliasedRegisters& aliases) const {
    if (insn->srcs_size() > 0 &&
        !opcode::has_range(insn->opcode()) && // range has to stay in order
        // we need to make sure the dest and src of check-cast stay identical,
        // because the dest is simply an alias to the src. See the comments in
        // IRInstruction.h for details.
        insn->opcode() != OPCODE_CHECK_CAST && !is_monitor(insn->opcode())) {
      for (size_t i = 0; i < insn->srcs_size(); ++i) {
        RegisterValue val{insn->src(i)};
        boost::optional<Register> rep = aliases.get_representative(val);
        if (rep) {
          // filter out uses of wide registers where the second register isn't
          // also aliased
          if (insn->src_is_wide(i)) {
            RegisterValue orig_wide{(Register)(insn->src(i) + 1)};
            RegisterValue rep_wide{(Register)(*rep + 1)};
            bool wides_are_aliased = aliases.are_aliases(orig_wide, rep_wide);
            if (!wides_are_aliased) {
              continue;
            }
          }

          if (insn->src(i) != *rep) {
            insn->set_src(i, *rep);
            m_stats.replaced_sources++;
          }
        }
      }
    }
  }

  const RegisterValue get_src_value(IRInstruction* insn) const {
    switch (insn->opcode()) {
    case OPCODE_MOVE:
    case OPCODE_MOVE_FROM16:
    case OPCODE_MOVE_16:
    case OPCODE_MOVE_OBJECT:
    case OPCODE_MOVE_OBJECT_FROM16:
    case OPCODE_MOVE_OBJECT_16:
      return RegisterValue{insn->src(0)};
    case OPCODE_CONST:
    case OPCODE_CONST_4:
    case OPCODE_CONST_16:
      if (m_config.eliminate_const_literals) {
        return RegisterValue{insn->literal()};
      } else {
        return RegisterValue::none();
      }
    case OPCODE_CONST_STRING:
    case OPCODE_CONST_STRING_JUMBO: {
      if (m_config.eliminate_const_strings) {
        DexString* str = insn->get_string();
        return RegisterValue{str};
      } else {
        return RegisterValue::none();
      }
    }
    case OPCODE_CONST_CLASS: {
      if (m_config.eliminate_const_classes) {
        DexType* type = insn->get_type();
        return RegisterValue{type};
      } else {
        return RegisterValue::none();
      }
    }
    default:
      return RegisterValue::none();
    }
  }

  void analyze_node(Block* const& node,
                    AliasDomain* current_state) const override {
    current_state->update([&](AliasedRegisters& aliases) {
      run_on_block(node, aliases, nullptr);
    });
  }

  AliasDomain analyze_edge(
      Block* const& /* source */,
      Block* const& /* target */,
      const AliasDomain& exit_state_at_source) const override {
    return exit_state_at_source;
  }
};

class RedundantMoveEliminationImpl final {
 public:
  explicit RedundantMoveEliminationImpl(
      const RedundantMoveEliminationPass::Config& config)
      : m_config(config) {}

  RedundantStats run(Scope scope) {
    using Data = std::nullptr_t;
    using Output = RedundantStats;
    return walk_methods_parallel<Data, Output>(
        scope,
        [this](Data&, DexMethod* m) {
          if (m->get_code()) {
            return run_on_method(m);
          }
          return RedundantStats();
        },
        [](Output a, Output b) { return a + b; },
        [](unsigned int /*thread_index*/) { return nullptr; });
  }

 private:
  const RedundantMoveEliminationPass::Config& m_config;

  RedundantStats run_on_method(DexMethod* method) {
    std::vector<IRInstruction*> deletes;
    RedundantStats stats;

    auto code = method->get_code();
    code->build_cfg();
    const auto& blocks = code->cfg().blocks();

    AliasFixpointIterator fixpoint(
        code->cfg().entry_block(),
        [](Block* const& block) { return block->succs(); },
        [](Block* const& block) { return block->preds(); },
        m_config,
        stats);

    if (m_config.full_method_analysis) {
      fixpoint.run(AliasDomain());
      for (auto block : blocks) {
        AliasDomain domain = fixpoint.get_entry_state_at(block);
        domain.update([&fixpoint, block, &deletes](AliasedRegisters& aliases) {
          fixpoint.run_on_block(block, aliases, &deletes);
        });
      }
    } else {
      for (auto block : blocks) {
        AliasedRegisters aliases;
        fixpoint.run_on_block(block, aliases, &deletes);
      }
    }

    stats.moves_eliminated += deletes.size();
    for (auto insn : deletes) {
      code->remove_opcode(insn);
    }
    return stats;
  }
};
}

void RedundantMoveEliminationPass::run_pass(DexStoresVector& stores,
                                            ConfigFiles& /* unused */,
                                            PassManager& mgr) {
  auto scope = build_class_scope(stores);

  if (m_config.eliminate_const_literals && !mgr.verify_none_enabled()) {
    // This option is not safe with the verifier
    m_config.eliminate_const_literals = false;
    TRACE(RME,
          1,
          "Ignoring eliminate_const_literals because verify-none is not "
          "enabled.\n");
  }

  RedundantMoveEliminationImpl impl(m_config);
  auto stats = impl.run(scope);
  mgr.incr_metric("redundant_moves_eliminated", stats.moves_eliminated);
  mgr.incr_metric("source_regs_replaced_with_representative",
                  stats.replaced_sources);
  TRACE(RME,
        1,
        "%d redundant moves eliminated\n",
        mgr.get_metric("redundant_moves_eliminated"));
  TRACE(RME,
        1,
        "%d source registers replaced with representative\n",
        mgr.get_metric("source_regs_replaced_with_representative"));
}

static RedundantMoveEliminationPass s_pass;
<|endoftext|>


<commit_before>/*****************************************************************************\
 *                        ANALYSIS PERFORMANCE TOOLS                         *
 *                               libparaver-api                              *
 *                      API Library for libparaver-kernel                    *
 *****************************************************************************
 *     ___     This library is free software; you can redistribute it and/or *
 *    /  __         modify it under the terms of the GNU LGPL as published   *
 *   /  /  _____    by the Free Software Foundation; either version 2.1      *
 *  /  /  /     \   of the License, or (at your option) any later version.   *
 * (  (  ( B S C )                                                           *
 *  \  \  \_____/   This library is distributed in hope that it will be      *
 *   \  \__         useful but WITHOUT ANY WARRANTY; without even the        *
 *    \___          implied warranty of MERCHANTABILITY or FITNESS FOR A     *
 *                  PARTICULAR PURPOSE. See the GNU LGPL for more details.   *
 *                                                                           *
 * You should have received a copy of the GNU Lesser General Public License  *
 * along with this library; if not, write to the Free Software Foundation,   *
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA          *
 * The GNU LEsser General Public License is contained in the file COPYING.   *
 *                                 ---------                                 *
 *   Barcelona Supercomputing Center - Centro Nacional de Supercomputacion   *
\*****************************************************************************/

#include "workspacemanager.h"
#include "paraverkernelexception.h"

#ifdef WIN32
  #include <shlobj.h>
  #include <Shlwapi.h>

  #define MAX_LEN_PATH 2048
#else
  #include <sys/stat.h>
  #include <pwd.h>
  #include <sys/types.h>
#endif

#ifdef __APPLE__
  #include "CoreFoundation/CoreFoundation.h"
#endif

using std::string;
using std::vector;

WorkspaceManager *WorkspaceManager::instance = NULL;

WorkspaceManager *WorkspaceManager::getInstance()
{
  if ( WorkspaceManager::instance == NULL )
    WorkspaceManager::instance = new WorkspaceManager();
  return WorkspaceManager::instance;
}

WorkspaceManager::WorkspaceManager()
{
}

WorkspaceManager::~WorkspaceManager()
{
}

void WorkspaceManager::clear()
{
  userWorkspaces.clear();
  userWorkspacesOrder.clear();
}

bool WorkspaceManager::existWorkspace( std::string name, TWorkspaceSet whichSet ) const
{
  switch ( whichSet )
  {
    case ALL:
      return ( distWorkspaces.find( name ) != distWorkspaces.end() ) ||
             ( userWorkspaces.find( name ) != userWorkspaces.end() );
      break;

    case DISTRIBUTED:
      return distWorkspaces.find( name ) != distWorkspaces.end();
      break;

    case USER_DEFINED:
      return userWorkspaces.find( name ) != userWorkspaces.end();
      break;

    default:
      throw ParaverKernelException();
      break;
  }

  return false;
}

vector<string> WorkspaceManager::getWorkspaces( TWorkspaceSet whichSet ) const
{
  vector< string > tmpWorkspacesOrder;

  switch ( whichSet )
  {
    case ALL:
      tmpWorkspacesOrder = distWorkspacesOrder;
      tmpWorkspacesOrder.insert( tmpWorkspacesOrder.end(), userWorkspacesOrder.begin(), userWorkspacesOrder.end() );
      return tmpWorkspacesOrder;
      break;

    case DISTRIBUTED:
      return distWorkspacesOrder;
      break;

    case USER_DEFINED:
      return userWorkspacesOrder;
      break;

    default:
      throw ParaverKernelException();
      break;
  }

  return vector< string >();
}

void WorkspaceManager::getMergedWorkspaces( const std::set<TState>& loadedStates,
                                            const std::set<TEventType>& loadedTypes,
                                            std::vector<std::string>& onWorkspaceVector,
                                            size_t& userDefined )
{
  vector<WorkspaceValue> tmpLoadedValues;
  for( std::set<TState>::const_iterator it = loadedStates.begin(); it != loadedStates.end(); ++it )
  {
    WorkspaceValue tmpWorkspaceValue;
    tmpWorkspaceValue.myType = WorkspaceValue::STATE;
    tmpWorkspaceValue.UInfo.state = *it;
    tmpLoadedValues.push_back( tmpWorkspaceValue );
  }

  for( std::set<TEventType>::const_iterator it = loadedTypes.begin(); it != loadedTypes.end(); ++it )
  {
    WorkspaceValue tmpWorkspaceValue;
    tmpWorkspaceValue.myType = WorkspaceValue::EVENT;
    tmpWorkspaceValue.UInfo.eventType = *it;
    tmpLoadedValues.push_back( tmpWorkspaceValue );
  }

  for( vector< string >::iterator it = distWorkspacesOrder.begin(); it != distWorkspacesOrder.end(); ++it )
  {
    vector< WorkspaceValue > tmpAutoTypes = distWorkspaces[ *it ].getAutoTypes();
    if ( find_first_of( tmpLoadedValues.begin(), tmpLoadedValues.end(),
                        tmpAutoTypes.begin(), tmpAutoTypes.end() ) !=  tmpLoadedValues.end() )
      onWorkspaceVector.push_back( *it );
  }
  userDefined = onWorkspaceVector.size();

  vector<string> tmpUserDefined;
  for( vector< string >::iterator it = userWorkspacesOrder.begin(); it != userWorkspacesOrder.end(); ++it )
  {
    vector< WorkspaceValue > tmpAutoTypes = userWorkspaces[ *it ].getAutoTypes();
    if( tmpAutoTypes.empty() )
      tmpUserDefined.push_back( *it );
    else if( find_first_of( tmpLoadedValues.begin(), tmpLoadedValues.end(),
                            tmpAutoTypes.begin(), tmpAutoTypes.end() ) !=  tmpLoadedValues.end() )
    {
      if( find( onWorkspaceVector.begin(), onWorkspaceVector.end(), *it ) != onWorkspaceVector.end() )
      {
        vector< WorkspaceValue > tmpDistAutoTypes = distWorkspaces[ *it ].getAutoTypes();
        sort( tmpDistAutoTypes.begin(), tmpDistAutoTypes.end() );
        vector< WorkspaceValue > tmpUserAutoTypes = userWorkspaces[ *it ].getAutoTypes();
        sort( tmpUserAutoTypes.begin(), tmpUserAutoTypes.end() );
        if( includes( tmpDistAutoTypes.begin(), tmpDistAutoTypes.end(),
                      tmpUserAutoTypes.begin(), tmpUserAutoTypes.end() ) )
          continue;
      }

      tmpUserDefined.push_back( *it );
    }
  }

  onWorkspaceVector.insert( onWorkspaceVector.end(), tmpUserDefined.begin(), tmpUserDefined.end() );
}

Workspace& WorkspaceManager::getWorkspace( std::string name, TWorkspaceSet whichSet )
{
  switch ( whichSet )
  {
    case ALL:
      if ( existWorkspace( name, DISTRIBUTED ) )
        return distWorkspaces[ name ];
      else
        return userWorkspaces[ name ];
      break;

    case DISTRIBUTED:
      return distWorkspaces[ name ];
      break;

    case USER_DEFINED:
      return userWorkspaces[ name ];
      break;

    default:
      throw ParaverKernelException();
      break;
  }

  return distWorkspaces["0"];
}

void WorkspaceManager::addWorkspace( std::string whichName )
{
  userWorkspaces[ whichName ] = Workspace();
  userWorkspacesOrder.push_back( whichName );
}

void WorkspaceManager::addWorkspace( Workspace& whichWorkspace )
{
  userWorkspaces[ whichWorkspace.getName() ] = whichWorkspace;
  userWorkspacesOrder.push_back( whichWorkspace.getName() );
}

void WorkspaceManager::loadXML()
{
  string baseDir;
  string fullPath;

  // Read distributed
#ifdef WIN32
  baseDir = getenv( "HOMEDRIVE" );
  baseDir.append( getenv( "HOMEPATH" ) );

  char myPath[ MAX_LEN_PATH ];
  HMODULE hModule = GetModuleHandle( NULL );
  if ( hModule != NULL )
  {
    GetModuleFileName( NULL, myPath, ( sizeof( myPath ) ));
    PathRemoveFileSpec( myPath );
    baseDir = myPath;
  }
#elif defined( __APPLE__ )
    CFBundleRef mainBundle = CFBundleGetMainBundle();
    CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
    char tmpPath[PATH_MAX];
    if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)tmpPath, PATH_MAX))
    {
        throw ParaverKernelException();
    }
    CFRelease(resourcesURL);

    baseDir = tmpPath;
#else
  if( getenv( "PARAVER_HOME" ) == NULL )
    baseDir = "";
  else
    baseDir = getenv( "PARAVER_HOME" );
#endif

  fullPath = baseDir;
#ifdef WIN32
  fullPath.append( "\\share\\workspaces" );
#else
  fullPath.append( "/share/workspaces" );
#endif
  fullPath.append( ".xml" );

  std::ifstream ifs( fullPath.c_str() );
  if( ifs.good() )
  {
    boost::archive::xml_iarchive ia( ifs );
    serializeBufferWorkspaces = &distWorkspaces;
    serializeBufferWorkspacesOrder = &distWorkspacesOrder;
    ia >> boost::serialization::make_nvp( "workspace_manager", *this );
  }
  ifs.close();

  // Read user defined
  ifs.clear();
  baseDir.clear();
  fullPath.clear();

#ifdef WIN32
  baseDir = getenv( "HOMEDRIVE" );
  baseDir.append( getenv( "HOMEPATH" ) );
#else
  baseDir = getenv( "HOME" );
#endif
  fullPath.append( baseDir );
#ifdef WIN32
  fullPath.append( "\\paraver\\workspaces" );
#else
  fullPath.append( "/.paraver/workspaces" );
#endif
  fullPath.append( ".xml" );

  ifs.open( fullPath.c_str() );
  if( !ifs.good() )
  {
    saveXML();
    ifs.open( fullPath.c_str() );
  }

  if( ifs.good() ) // due to xml_iarchive needs to be destroyed before fstream is closed
  {
    boost::archive::xml_iarchive ia( ifs );
    serializeBufferWorkspaces = &userWorkspaces;
    serializeBufferWorkspacesOrder = &userWorkspacesOrder;
    ia >> boost::serialization::make_nvp( "workspace_manager", *this );
  }
  ifs.close();
}

void WorkspaceManager::saveXML()
{
  string homedir;
  string strFile;

#ifdef WIN32
  homedir = getenv( "HOMEDRIVE" );
  homedir.append( getenv( "HOMEPATH" ) );
#else
  homedir = getenv( "HOME" );
#endif
  strFile.append( homedir );

#ifdef WIN32
  strFile.append( "\\paraver\\workspaces" );
  string tmpPath( homedir + "\\workspaces" );
/*
  int len = tmpPath.length() + 1;
  wchar_t *wText = new wchar_t[len];
  memset(wText,0,len);
  ::MultiByteToWideChar( CP_ACP, NULL, tmpPath.c_str(), -1, wText, len );
*/
  SHCreateDirectoryEx( NULL, tmpPath.c_str(), NULL );
  //delete []wText;
#else
  strFile.append( "/.paraver/workspaces" );
  mkdir( ( homedir + "/.paraver" ).c_str(), (mode_t)0700 );
#endif
  strFile.append( ".xml" );

  std::ofstream ofs( strFile.c_str() );
  if( ofs.good() ) // due to xml_oarchive needs to be destroyed before fstream is closed
  {
    boost::archive::xml_oarchive oa( ofs );
    serializeBufferWorkspaces = &userWorkspaces;
    serializeBufferWorkspacesOrder = &userWorkspacesOrder;
    oa << boost::serialization::make_nvp( "workspace_manager", *this );
  }
  ofs.close();
}

<commit_msg>Fixed workspaces default directory for windows<commit_after>/*****************************************************************************\
 *                        ANALYSIS PERFORMANCE TOOLS                         *
 *                               libparaver-api                              *
 *                      API Library for libparaver-kernel                    *
 *****************************************************************************
 *     ___     This library is free software; you can redistribute it and/or *
 *    /  __         modify it under the terms of the GNU LGPL as published   *
 *   /  /  _____    by the Free Software Foundation; either version 2.1      *
 *  /  /  /     \   of the License, or (at your option) any later version.   *
 * (  (  ( B S C )                                                           *
 *  \  \  \_____/   This library is distributed in hope that it will be      *
 *   \  \__         useful but WITHOUT ANY WARRANTY; without even the        *
 *    \___          implied warranty of MERCHANTABILITY or FITNESS FOR A     *
 *                  PARTICULAR PURPOSE. See the GNU LGPL for more details.   *
 *                                                                           *
 * You should have received a copy of the GNU Lesser General Public License  *
 * along with this library; if not, write to the Free Software Foundation,   *
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA          *
 * The GNU LEsser General Public License is contained in the file COPYING.   *
 *                                 ---------                                 *
 *   Barcelona Supercomputing Center - Centro Nacional de Supercomputacion   *
\*****************************************************************************/

#include "workspacemanager.h"
#include "paraverkernelexception.h"

#ifdef WIN32
  #include <shlobj.h>
  #include <Shlwapi.h>

  #define MAX_LEN_PATH 2048
#else
  #include <sys/stat.h>
  #include <pwd.h>
  #include <sys/types.h>
#endif

#ifdef __APPLE__
  #include "CoreFoundation/CoreFoundation.h"
#endif

using std::string;
using std::vector;

WorkspaceManager *WorkspaceManager::instance = NULL;

WorkspaceManager *WorkspaceManager::getInstance()
{
  if ( WorkspaceManager::instance == NULL )
    WorkspaceManager::instance = new WorkspaceManager();
  return WorkspaceManager::instance;
}

WorkspaceManager::WorkspaceManager()
{
}

WorkspaceManager::~WorkspaceManager()
{
}

void WorkspaceManager::clear()
{
  userWorkspaces.clear();
  userWorkspacesOrder.clear();
}

bool WorkspaceManager::existWorkspace( std::string name, TWorkspaceSet whichSet ) const
{
  switch ( whichSet )
  {
    case ALL:
      return ( distWorkspaces.find( name ) != distWorkspaces.end() ) ||
             ( userWorkspaces.find( name ) != userWorkspaces.end() );
      break;

    case DISTRIBUTED:
      return distWorkspaces.find( name ) != distWorkspaces.end();
      break;

    case USER_DEFINED:
      return userWorkspaces.find( name ) != userWorkspaces.end();
      break;

    default:
      throw ParaverKernelException();
      break;
  }

  return false;
}

vector<string> WorkspaceManager::getWorkspaces( TWorkspaceSet whichSet ) const
{
  vector< string > tmpWorkspacesOrder;

  switch ( whichSet )
  {
    case ALL:
      tmpWorkspacesOrder = distWorkspacesOrder;
      tmpWorkspacesOrder.insert( tmpWorkspacesOrder.end(), userWorkspacesOrder.begin(), userWorkspacesOrder.end() );
      return tmpWorkspacesOrder;
      break;

    case DISTRIBUTED:
      return distWorkspacesOrder;
      break;

    case USER_DEFINED:
      return userWorkspacesOrder;
      break;

    default:
      throw ParaverKernelException();
      break;
  }

  return vector< string >();
}

void WorkspaceManager::getMergedWorkspaces( const std::set<TState>& loadedStates,
                                            const std::set<TEventType>& loadedTypes,
                                            std::vector<std::string>& onWorkspaceVector,
                                            size_t& userDefined )
{
  vector<WorkspaceValue> tmpLoadedValues;
  for( std::set<TState>::const_iterator it = loadedStates.begin(); it != loadedStates.end(); ++it )
  {
    WorkspaceValue tmpWorkspaceValue;
    tmpWorkspaceValue.myType = WorkspaceValue::STATE;
    tmpWorkspaceValue.UInfo.state = *it;
    tmpLoadedValues.push_back( tmpWorkspaceValue );
  }

  for( std::set<TEventType>::const_iterator it = loadedTypes.begin(); it != loadedTypes.end(); ++it )
  {
    WorkspaceValue tmpWorkspaceValue;
    tmpWorkspaceValue.myType = WorkspaceValue::EVENT;
    tmpWorkspaceValue.UInfo.eventType = *it;
    tmpLoadedValues.push_back( tmpWorkspaceValue );
  }

  for( vector< string >::iterator it = distWorkspacesOrder.begin(); it != distWorkspacesOrder.end(); ++it )
  {
    vector< WorkspaceValue > tmpAutoTypes = distWorkspaces[ *it ].getAutoTypes();
    if ( find_first_of( tmpLoadedValues.begin(), tmpLoadedValues.end(),
                        tmpAutoTypes.begin(), tmpAutoTypes.end() ) !=  tmpLoadedValues.end() )
      onWorkspaceVector.push_back( *it );
  }
  userDefined = onWorkspaceVector.size();

  vector<string> tmpUserDefined;
  for( vector< string >::iterator it = userWorkspacesOrder.begin(); it != userWorkspacesOrder.end(); ++it )
  {
    vector< WorkspaceValue > tmpAutoTypes = userWorkspaces[ *it ].getAutoTypes();
    if( tmpAutoTypes.empty() )
      tmpUserDefined.push_back( *it );
    else if( find_first_of( tmpLoadedValues.begin(), tmpLoadedValues.end(),
                            tmpAutoTypes.begin(), tmpAutoTypes.end() ) !=  tmpLoadedValues.end() )
    {
      if( find( onWorkspaceVector.begin(), onWorkspaceVector.end(), *it ) != onWorkspaceVector.end() )
      {
        vector< WorkspaceValue > tmpDistAutoTypes = distWorkspaces[ *it ].getAutoTypes();
        sort( tmpDistAutoTypes.begin(), tmpDistAutoTypes.end() );
        vector< WorkspaceValue > tmpUserAutoTypes = userWorkspaces[ *it ].getAutoTypes();
        sort( tmpUserAutoTypes.begin(), tmpUserAutoTypes.end() );
        if( includes( tmpDistAutoTypes.begin(), tmpDistAutoTypes.end(),
                      tmpUserAutoTypes.begin(), tmpUserAutoTypes.end() ) )
          continue;
      }

      tmpUserDefined.push_back( *it );
    }
  }

  onWorkspaceVector.insert( onWorkspaceVector.end(), tmpUserDefined.begin(), tmpUserDefined.end() );
}

Workspace& WorkspaceManager::getWorkspace( std::string name, TWorkspaceSet whichSet )
{
  switch ( whichSet )
  {
    case ALL:
      if ( existWorkspace( name, DISTRIBUTED ) )
        return distWorkspaces[ name ];
      else
        return userWorkspaces[ name ];
      break;

    case DISTRIBUTED:
      return distWorkspaces[ name ];
      break;

    case USER_DEFINED:
      return userWorkspaces[ name ];
      break;

    default:
      throw ParaverKernelException();
      break;
  }

  return distWorkspaces["0"];
}

void WorkspaceManager::addWorkspace( std::string whichName )
{
  userWorkspaces[ whichName ] = Workspace();
  userWorkspacesOrder.push_back( whichName );
}

void WorkspaceManager::addWorkspace( Workspace& whichWorkspace )
{
  userWorkspaces[ whichWorkspace.getName() ] = whichWorkspace;
  userWorkspacesOrder.push_back( whichWorkspace.getName() );
}

void WorkspaceManager::loadXML()
{
  string baseDir;
  string fullPath;

  // Read distributed
#ifdef WIN32
  baseDir = getenv( "HOMEDRIVE" );
  baseDir.append( getenv( "HOMEPATH" ) );

  char myPath[ MAX_LEN_PATH ];
  HMODULE hModule = GetModuleHandle( NULL );
  if ( hModule != NULL )
  {
    GetModuleFileName( NULL, myPath, ( sizeof( myPath ) ));
    PathRemoveFileSpec( myPath );
    string tmpParaverPath( myPath );
    baseDir = tmpParaverPath.substr( 0, tmpParaverPath.size() - 4 );
  }
#elif defined( __APPLE__ )
    CFBundleRef mainBundle = CFBundleGetMainBundle();
    CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
    char tmpPath[PATH_MAX];
    if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)tmpPath, PATH_MAX))
    {
        throw ParaverKernelException();
    }
    CFRelease(resourcesURL);

    baseDir = tmpPath;
#else
  if( getenv( "PARAVER_HOME" ) == NULL )
    baseDir = "";
  else
    baseDir = getenv( "PARAVER_HOME" );
#endif

  fullPath = baseDir;
#ifdef WIN32
  fullPath.append( "\\share\\workspaces.xml" );
#else
  fullPath.append( "/share/workspaces.xml" );
#endif

  std::ifstream ifs( fullPath.c_str() );
  if( ifs.good() )
  {
    boost::archive::xml_iarchive ia( ifs );
    serializeBufferWorkspaces = &distWorkspaces;
    serializeBufferWorkspacesOrder = &distWorkspacesOrder;
    ia >> boost::serialization::make_nvp( "workspace_manager", *this );
  }
  ifs.close();

  // Read user defined
  ifs.clear();
  baseDir.clear();
  fullPath.clear();

#ifdef WIN32
  baseDir = getenv( "HOMEDRIVE" );
  baseDir.append( getenv( "HOMEPATH" ) );
#else
  baseDir = getenv( "HOME" );
#endif
  fullPath.append( baseDir );
#ifdef WIN32
  fullPath.append( "\\paraver\\workspaces" );
#else
  fullPath.append( "/.paraver/workspaces" );
#endif
  fullPath.append( ".xml" );

  ifs.open( fullPath.c_str() );
  if( !ifs.good() )
  {
    saveXML();
    ifs.open( fullPath.c_str() );
  }

  if( ifs.good() ) // due to xml_iarchive needs to be destroyed before fstream is closed
  {
    boost::archive::xml_iarchive ia( ifs );
    serializeBufferWorkspaces = &userWorkspaces;
    serializeBufferWorkspacesOrder = &userWorkspacesOrder;
    ia >> boost::serialization::make_nvp( "workspace_manager", *this );
  }
  ifs.close();
}

void WorkspaceManager::saveXML()
{
  string homedir;
  string strFile;

#ifdef WIN32
  homedir = getenv( "HOMEDRIVE" );
  homedir.append( getenv( "HOMEPATH" ) );
#else
  homedir = getenv( "HOME" );
#endif
  strFile.append( homedir );

#ifdef WIN32
  strFile.append( "\\paraver\\workspaces" );
  string tmpPath( homedir + "\\workspaces" );
/*
  int len = tmpPath.length() + 1;
  wchar_t *wText = new wchar_t[len];
  memset(wText,0,len);
  ::MultiByteToWideChar( CP_ACP, NULL, tmpPath.c_str(), -1, wText, len );
*/
  SHCreateDirectoryEx( NULL, tmpPath.c_str(), NULL );
  //delete []wText;
#else
  strFile.append( "/.paraver/workspaces" );
  mkdir( ( homedir + "/.paraver" ).c_str(), (mode_t)0700 );
#endif
  strFile.append( ".xml" );

  std::ofstream ofs( strFile.c_str() );
  if( ofs.good() ) // due to xml_oarchive needs to be destroyed before fstream is closed
  {
    boost::archive::xml_oarchive oa( ofs );
    serializeBufferWorkspaces = &userWorkspaces;
    serializeBufferWorkspacesOrder = &userWorkspacesOrder;
    oa << boost::serialization::make_nvp( "workspace_manager", *this );
  }
  ofs.close();
}

<|endoftext|>