<commit_before>/****************************************************************/
/*               DO NOT MODIFY THIS HEADER                      */
/* MOOSE - Multiphysics Object Oriented Simulation Environment  */
/*                                                              */
/*           (c) 2010 Battelle Energy Alliance, LLC             */
/*                   ALL RIGHTS RESERVED                        */
/*                                                              */
/*          Prepared by Battelle Energy Alliance, LLC           */
/*            Under Contract No. DE-AC07-05ID14517              */
/*            With the U. S. Department of Energy               */
/*                                                              */
/*            See COPYRIGHT for full restrictions               */
/****************************************************************/

#include "ActionWarehouse.h"
#include "ActionFactory.h"
#include "Parser.h"
#include "MooseObjectAction.h"
#include "InputFileFormatter.h"
#include "InputParameters.h"
#include "MooseMesh.h"
#include "AddVariableAction.h"
#include "AddAuxVariableAction.h"
#include "XTermConstants.h"
#include "InfixIterator.h"

ActionWarehouse::ActionWarehouse(MooseApp & app, Syntax & syntax, ActionFactory & factory) :
    ConsoleStreamInterface(app),
    _app(app),
    _syntax(syntax),
    _action_factory(factory),
    _generator_valid(false),
    _show_actions(false),
    _show_parser(false)
{
}

ActionWarehouse::~ActionWarehouse()
{
}

void
ActionWarehouse::build()
{
  _ordered_names = _syntax.getSortedTask();
  for (const auto & name : _ordered_names)
    buildBuildableActions(name);
}

void
ActionWarehouse::clear()
{
  for (auto & ptr : _all_ptrs)
    ptr.reset();

  _action_blocks.clear();
  _generator_valid = false;

  // Due to the way ActionWarehouse is cleaned up (see MooseApp's
  // destructor) we must guarantee that ActionWarehouse::clear()
  // releases all the resources which have to be released _before_ the
  // _comm object owned by the MooseApp is destroyed.
  _mesh.reset();
  _displaced_mesh.reset();

  _problem.reset();
}

void
ActionWarehouse::addActionBlock(MooseSharedPointer<Action> action)
{
  /**
   * Note: This routine uses the XTerm colors directly which is not advised for general purpose output coloring.
   * Most users should prefer using Problem::colorText() which respects the "color_output" option for terminals
   * that do not support coloring.  Since this routine is intended for debugging only and runs before several
   * objects exist in the system, we are just using the constants directly.
   */

  std::string registered_identifier = action->parameters().get<std::string>("registered_identifier");
  std::set<std::string> tasks;

  if (_show_parser)
    Moose::err << COLOR_DEFAULT << "Parsing Syntax:        " << COLOR_GREEN   << action->name() << '\n'
               << COLOR_DEFAULT << "Building Action:       " << COLOR_DEFAULT << action->type() << '\n'
               << COLOR_DEFAULT << "Registered Identifier: " << COLOR_GREEN   << registered_identifier << '\n'
               << COLOR_DEFAULT << "Specific Task:         " << COLOR_CYAN    << action->specificTaskName() << '\n';

  /**
   * We need to see if the current Action satisfies multiple tasks. There are a few cases to consider:
   *
   * 1. The current Action is registered with multiple syntax blocks. In this case we can only use the
   *    current instance to satisfy the specific task listed for this syntax block.  We can detect this
   *    case by inspecting whether it has a "specific task name" set in the Action instance.
   *
   * 2. This action does not have a valid "registered identifier" set in the Action instance. This means
   *    that this Action was not built by the Parser.  It was most likely created through a Meta-Action.
   *    In this case, the ActionFactory itself would have already set the task it found from the build
   *    info used to construct the Action.
   *
   * 3. The current Action is registered with only a single syntax block. In this case we can simply
   *    re-use the current instance to act and satisfy _all_ registered tasks. This is the normal
   *    case where we have a Parser-built Action that does not have a specific task name to satisfy.
   *    We will use the Action "type" to retrieve the list of tasks that this Action may satisfy.
   */
  if (action->specificTaskName() != "")           // Case 1
    tasks.insert(action->specificTaskName());
  else if (registered_identifier == "")           // Case 2
  {
    std::set<std::string> local_tasks = action->getAllTasks();
    mooseAssert(local_tasks.size() == 1, "More than one task inside of the " << action->name());
    tasks.insert(*local_tasks.begin());
  }
  else                                            // Case 3
    tasks = _action_factory.getTasksByAction(action->type());

  //TODO: Now we need to weed out the double registrations!
  for (const auto & task : tasks)
  {
    // Some error checking
    if (!_syntax.hasTask(task))
      mooseError("A(n) " << task << " is not a registered task");

    // Make sure that the ObjectAction task and Action task are consistent
    // otherwise that means that is action was built by the wrong type
    MooseSharedPointer<MooseObjectAction> moa = MooseSharedNamespace::dynamic_pointer_cast<MooseObjectAction>(action);
    if (moa.get())
    {
      const InputParameters & mparams = moa->getObjectParams();

      if (mparams.have_parameter<std::string>("_moose_base"))
      {
        const std::string & base = mparams.get<std::string>("_moose_base");

        if (!_syntax.verifyMooseObjectTask(base, task))
          mooseError("Task " << task << " is not registered to build " << base << " derived objects");
      }
      else
        mooseError("Unable to locate registered base parameter for " << moa->getMooseObjectType());
    }

    // Add the current task to current action
    action->appendTask(task);

    if (_show_parser)
      Moose::err << COLOR_YELLOW << "Adding Action:         " << COLOR_DEFAULT << action->type() << " (" << COLOR_YELLOW << task << COLOR_DEFAULT << ")\n";

    // Add it to the warehouse
    _all_ptrs.push_back(action);
    _action_blocks[task].push_back(action.get());
  }

  if (_show_parser)
    Moose::err << std::endl;
}

ActionIterator
ActionWarehouse::actionBlocksWithActionBegin(const std::string & task)
{
  return _action_blocks[task].begin();
}

ActionIterator
ActionWarehouse::actionBlocksWithActionEnd(const std::string & task)
{
  return _action_blocks[task].end();
}

const std::vector<Action *> &
ActionWarehouse::getActionsByName(const std::string & task) const
{
  std::map<std::string, std::vector<Action *> >::const_iterator it = _action_blocks.find(task);
  if (it == _action_blocks.end())
    mooseError("The task " << task << " does not exist.");
  return it->second;
}

bool
ActionWarehouse::hasActions(const std::string & task) const
{
  return _action_blocks.find(task) != _action_blocks.end();
}

void
ActionWarehouse::buildBuildableActions(const std::string &task)
{
  if (_syntax.isActionRequired(task) && _action_blocks[task].empty())
  {
    bool ret_value = false;
    std::pair<std::multimap<std::string, std::string>::const_iterator,
              std::multimap<std::string, std::string>::const_iterator> range = _action_factory.getActionsByTask(task);
    for (std::multimap<std::string, std::string>::const_iterator it = range.first; it != range.second; ++it)
    {
      InputParameters params = _action_factory.getValidParams(it->second);
      params.set<ActionWarehouse *>("awh") = this;

      if (params.areAllRequiredParamsValid())
      {
        params.set<std::string>("registered_identifier") = "(AutoBuilt)";
        params.set<std::string>("task") = task;
        addActionBlock(_action_factory.create(it->second, "", params));
        ret_value = true;
      }
    }

    if (!ret_value)
      _unsatisfied_dependencies.insert(task);
  }
}

void
ActionWarehouse::checkUnsatisfiedActions() const
{
  std::stringstream oss;
  bool empty = true;

  for (const auto & udep : _unsatisfied_dependencies)
  {
    if (_action_blocks.find(udep) == _action_blocks.end())
    {
      if (empty)
        empty = false;
      else
        oss << " ";
      oss << udep;
    }
  }

  if (!empty)
    mooseError(std::string("The following unsatisfied actions where found while setting up the MOOSE problem:\n")
               + oss.str() + "\n");
}

void
ActionWarehouse::printActionDependencySets() const
{
  /**
   * Note: This routine uses the XTerm colors directly which is not advised for general purpose output coloring.
   * Most users should prefer using Problem::colorText() which respects the "color_output" option for terminals
   * that do not support coloring.  Since this routine is intended for debugging only and runs before several
   * objects exist in the system, we are just using the constants directly.
   */
  std::ostringstream oss;

  const std::vector<std::set<std::string> > & ordered_names = _syntax.getSortedTaskSet();
  for (const auto & task_set : ordered_names)
  {
    oss << "[DBG][ACT] (" << COLOR_YELLOW;
    std::copy(task_set.begin(), task_set.end(), infix_ostream_iterator<std::string>(oss, ", "));
    oss << COLOR_DEFAULT << ")\n";

    for (const auto & task : task_set)
    {
      if (_action_blocks.find(task) == _action_blocks.end())
        continue;

      for (const auto & act : _action_blocks.at(task))
      {
        // The Syntax of the Action if it exists
        if (act->name() != "")
          oss << "[DBG][ACT]\t" << COLOR_GREEN << act->name() << COLOR_DEFAULT << '\n';

        // The task sets
        oss << "[DBG][ACT]\t" << act->type();
        const std::set<std::string> tasks = act->getAllTasks();
        if (tasks.size() > 1)
        {
          oss << " (";
          // Break the current Action's tasks into 2 sets, those intersecting with current set and then the difference.
          std::set<std::string> intersection, difference;
          std::set_intersection(tasks.begin(), tasks.end(), task_set.begin(), task_set.end(),
                                std::inserter(intersection, intersection.end()));
          std::set_difference(tasks.begin(), tasks.end(), intersection.begin(), intersection.end(),
                              std::inserter(difference, difference.end()));

          oss << COLOR_CYAN;
          std::copy(intersection.begin(), intersection.end(), infix_ostream_iterator<std::string>(oss, ", "));
          oss << COLOR_MAGENTA << (difference.empty() ? "" : ", ");
          std::copy(difference.begin(), difference.end(), infix_ostream_iterator<std::string>(oss, ", "));
          oss << COLOR_DEFAULT << ")";
        }
        oss << '\n';
      }
    }
  }

  if (_show_actions)
    _console << oss.str() << std::endl;
}

void
ActionWarehouse::executeAllActions()
{
  if (_show_actions)
  {
    _console << "[DBG][ACT] Action Dependency Sets:\n";
    printActionDependencySets();

    _console << "\n[DBG][ACT] Executing actions:" << std::endl;
  }

  for (const auto & task : _ordered_names)
    executeActionsWithAction(task);
}

void
ActionWarehouse::executeActionsWithAction(const std::string & task)
{
  // Set the current task name
  _current_task = task;

  for (ActionIterator act_iter = actionBlocksWithActionBegin(task);
       act_iter != actionBlocksWithActionEnd(task);
       ++act_iter)
  {
    if (_show_actions)
    {
      _console << "[DBG][ACT] "
               << "TASK (" << COLOR_YELLOW << std::setw (24) << task << COLOR_DEFAULT << ") "
               << "TYPE (" << COLOR_YELLOW << std::setw (32) << (*act_iter)->type() << COLOR_DEFAULT << ") "
               << "NAME (" << COLOR_YELLOW << std::setw (16) << (*act_iter)->name() << COLOR_DEFAULT << ") \n";

      (*act_iter)->act();
    }
    else
      (*act_iter)->act();
  }
}

void
ActionWarehouse::printInputFile(std::ostream & out)
{
  InputFileFormatter tree(false);

  std::map<std::string, std::vector<Action *> >::iterator iter;

  std::vector<Action *> ordered_actions;
  for (const auto & block : _action_blocks)
    for (const auto & act : block.second)
      ordered_actions.push_back(act);

  for (const auto & act : ordered_actions)
  {
    std::string name;
    if (act->isParamValid("parser_syntax"))
      name = act->getParam<std::string>("parser_syntax");
    else
      name = act->name();
    const std::set<std::string> & tasks = act->getAllTasks();
    mooseAssert(!tasks.empty(), "Task list is empty");

    bool is_parent;
    if (_syntax.isAssociated(name, &is_parent) != "")
     {
      InputParameters params = act->parameters();

      // TODO: Do we need to insert more nodes for each task?
      tree.insertNode(name, *tasks.begin(), true, &params);

      MooseObjectAction * moose_object_action = dynamic_cast<MooseObjectAction *>(act);
      if (moose_object_action)
       {
        InputParameters obj_params = moose_object_action->getObjectParams();
        tree.insertNode(name, *tasks.begin(), false, &obj_params);
       }
     }
  }

  out << tree.print("");
}
<commit_msg>Destroy the mesh only after the problem using it<commit_after>/****************************************************************/
/*               DO NOT MODIFY THIS HEADER                      */
/* MOOSE - Multiphysics Object Oriented Simulation Environment  */
/*                                                              */
/*           (c) 2010 Battelle Energy Alliance, LLC             */
/*                   ALL RIGHTS RESERVED                        */
/*                                                              */
/*          Prepared by Battelle Energy Alliance, LLC           */
/*            Under Contract No. DE-AC07-05ID14517              */
/*            With the U. S. Department of Energy               */
/*                                                              */
/*            See COPYRIGHT for full restrictions               */
/****************************************************************/

#include "ActionWarehouse.h"
#include "ActionFactory.h"
#include "Parser.h"
#include "MooseObjectAction.h"
#include "InputFileFormatter.h"
#include "InputParameters.h"
#include "MooseMesh.h"
#include "AddVariableAction.h"
#include "AddAuxVariableAction.h"
#include "XTermConstants.h"
#include "InfixIterator.h"

ActionWarehouse::ActionWarehouse(MooseApp & app, Syntax & syntax, ActionFactory & factory) :
    ConsoleStreamInterface(app),
    _app(app),
    _syntax(syntax),
    _action_factory(factory),
    _generator_valid(false),
    _show_actions(false),
    _show_parser(false)
{
}

ActionWarehouse::~ActionWarehouse()
{
}

void
ActionWarehouse::build()
{
  _ordered_names = _syntax.getSortedTask();
  for (const auto & name : _ordered_names)
    buildBuildableActions(name);
}

void
ActionWarehouse::clear()
{
  for (auto & ptr : _all_ptrs)
    ptr.reset();

  _action_blocks.clear();
  _generator_valid = false;

  // Due to the way ActionWarehouse is cleaned up (see MooseApp's
  // destructor) we must guarantee that ActionWarehouse::clear()
  // releases all the resources which have to be released _before_ the
  // _comm object owned by the MooseApp is destroyed.
  _problem.reset();
  _displaced_mesh.reset();
  _mesh.reset();
}

void
ActionWarehouse::addActionBlock(MooseSharedPointer<Action> action)
{
  /**
   * Note: This routine uses the XTerm colors directly which is not advised for general purpose output coloring.
   * Most users should prefer using Problem::colorText() which respects the "color_output" option for terminals
   * that do not support coloring.  Since this routine is intended for debugging only and runs before several
   * objects exist in the system, we are just using the constants directly.
   */

  std::string registered_identifier = action->parameters().get<std::string>("registered_identifier");
  std::set<std::string> tasks;

  if (_show_parser)
    Moose::err << COLOR_DEFAULT << "Parsing Syntax:        " << COLOR_GREEN   << action->name() << '\n'
               << COLOR_DEFAULT << "Building Action:       " << COLOR_DEFAULT << action->type() << '\n'
               << COLOR_DEFAULT << "Registered Identifier: " << COLOR_GREEN   << registered_identifier << '\n'
               << COLOR_DEFAULT << "Specific Task:         " << COLOR_CYAN    << action->specificTaskName() << '\n';

  /**
   * We need to see if the current Action satisfies multiple tasks. There are a few cases to consider:
   *
   * 1. The current Action is registered with multiple syntax blocks. In this case we can only use the
   *    current instance to satisfy the specific task listed for this syntax block.  We can detect this
   *    case by inspecting whether it has a "specific task name" set in the Action instance.
   *
   * 2. This action does not have a valid "registered identifier" set in the Action instance. This means
   *    that this Action was not built by the Parser.  It was most likely created through a Meta-Action.
   *    In this case, the ActionFactory itself would have already set the task it found from the build
   *    info used to construct the Action.
   *
   * 3. The current Action is registered with only a single syntax block. In this case we can simply
   *    re-use the current instance to act and satisfy _all_ registered tasks. This is the normal
   *    case where we have a Parser-built Action that does not have a specific task name to satisfy.
   *    We will use the Action "type" to retrieve the list of tasks that this Action may satisfy.
   */
  if (action->specificTaskName() != "")           // Case 1
    tasks.insert(action->specificTaskName());
  else if (registered_identifier == "")           // Case 2
  {
    std::set<std::string> local_tasks = action->getAllTasks();
    mooseAssert(local_tasks.size() == 1, "More than one task inside of the " << action->name());
    tasks.insert(*local_tasks.begin());
  }
  else                                            // Case 3
    tasks = _action_factory.getTasksByAction(action->type());

  //TODO: Now we need to weed out the double registrations!
  for (const auto & task : tasks)
  {
    // Some error checking
    if (!_syntax.hasTask(task))
      mooseError("A(n) " << task << " is not a registered task");

    // Make sure that the ObjectAction task and Action task are consistent
    // otherwise that means that is action was built by the wrong type
    MooseSharedPointer<MooseObjectAction> moa = MooseSharedNamespace::dynamic_pointer_cast<MooseObjectAction>(action);
    if (moa.get())
    {
      const InputParameters & mparams = moa->getObjectParams();

      if (mparams.have_parameter<std::string>("_moose_base"))
      {
        const std::string & base = mparams.get<std::string>("_moose_base");

        if (!_syntax.verifyMooseObjectTask(base, task))
          mooseError("Task " << task << " is not registered to build " << base << " derived objects");
      }
      else
        mooseError("Unable to locate registered base parameter for " << moa->getMooseObjectType());
    }

    // Add the current task to current action
    action->appendTask(task);

    if (_show_parser)
      Moose::err << COLOR_YELLOW << "Adding Action:         " << COLOR_DEFAULT << action->type() << " (" << COLOR_YELLOW << task << COLOR_DEFAULT << ")\n";

    // Add it to the warehouse
    _all_ptrs.push_back(action);
    _action_blocks[task].push_back(action.get());
  }

  if (_show_parser)
    Moose::err << std::endl;
}

ActionIterator
ActionWarehouse::actionBlocksWithActionBegin(const std::string & task)
{
  return _action_blocks[task].begin();
}

ActionIterator
ActionWarehouse::actionBlocksWithActionEnd(const std::string & task)
{
  return _action_blocks[task].end();
}

const std::vector<Action *> &
ActionWarehouse::getActionsByName(const std::string & task) const
{
  std::map<std::string, std::vector<Action *> >::const_iterator it = _action_blocks.find(task);
  if (it == _action_blocks.end())
    mooseError("The task " << task << " does not exist.");
  return it->second;
}

bool
ActionWarehouse::hasActions(const std::string & task) const
{
  return _action_blocks.find(task) != _action_blocks.end();
}

void
ActionWarehouse::buildBuildableActions(const std::string &task)
{
  if (_syntax.isActionRequired(task) && _action_blocks[task].empty())
  {
    bool ret_value = false;
    std::pair<std::multimap<std::string, std::string>::const_iterator,
              std::multimap<std::string, std::string>::const_iterator> range = _action_factory.getActionsByTask(task);
    for (std::multimap<std::string, std::string>::const_iterator it = range.first; it != range.second; ++it)
    {
      InputParameters params = _action_factory.getValidParams(it->second);
      params.set<ActionWarehouse *>("awh") = this;

      if (params.areAllRequiredParamsValid())
      {
        params.set<std::string>("registered_identifier") = "(AutoBuilt)";
        params.set<std::string>("task") = task;
        addActionBlock(_action_factory.create(it->second, "", params));
        ret_value = true;
      }
    }

    if (!ret_value)
      _unsatisfied_dependencies.insert(task);
  }
}

void
ActionWarehouse::checkUnsatisfiedActions() const
{
  std::stringstream oss;
  bool empty = true;

  for (const auto & udep : _unsatisfied_dependencies)
  {
    if (_action_blocks.find(udep) == _action_blocks.end())
    {
      if (empty)
        empty = false;
      else
        oss << " ";
      oss << udep;
    }
  }

  if (!empty)
    mooseError(std::string("The following unsatisfied actions where found while setting up the MOOSE problem:\n")
               + oss.str() + "\n");
}

void
ActionWarehouse::printActionDependencySets() const
{
  /**
   * Note: This routine uses the XTerm colors directly which is not advised for general purpose output coloring.
   * Most users should prefer using Problem::colorText() which respects the "color_output" option for terminals
   * that do not support coloring.  Since this routine is intended for debugging only and runs before several
   * objects exist in the system, we are just using the constants directly.
   */
  std::ostringstream oss;

  const std::vector<std::set<std::string> > & ordered_names = _syntax.getSortedTaskSet();
  for (const auto & task_set : ordered_names)
  {
    oss << "[DBG][ACT] (" << COLOR_YELLOW;
    std::copy(task_set.begin(), task_set.end(), infix_ostream_iterator<std::string>(oss, ", "));
    oss << COLOR_DEFAULT << ")\n";

    for (const auto & task : task_set)
    {
      if (_action_blocks.find(task) == _action_blocks.end())
        continue;

      for (const auto & act : _action_blocks.at(task))
      {
        // The Syntax of the Action if it exists
        if (act->name() != "")
          oss << "[DBG][ACT]\t" << COLOR_GREEN << act->name() << COLOR_DEFAULT << '\n';

        // The task sets
        oss << "[DBG][ACT]\t" << act->type();
        const std::set<std::string> tasks = act->getAllTasks();
        if (tasks.size() > 1)
        {
          oss << " (";
          // Break the current Action's tasks into 2 sets, those intersecting with current set and then the difference.
          std::set<std::string> intersection, difference;
          std::set_intersection(tasks.begin(), tasks.end(), task_set.begin(), task_set.end(),
                                std::inserter(intersection, intersection.end()));
          std::set_difference(tasks.begin(), tasks.end(), intersection.begin(), intersection.end(),
                              std::inserter(difference, difference.end()));

          oss << COLOR_CYAN;
          std::copy(intersection.begin(), intersection.end(), infix_ostream_iterator<std::string>(oss, ", "));
          oss << COLOR_MAGENTA << (difference.empty() ? "" : ", ");
          std::copy(difference.begin(), difference.end(), infix_ostream_iterator<std::string>(oss, ", "));
          oss << COLOR_DEFAULT << ")";
        }
        oss << '\n';
      }
    }
  }

  if (_show_actions)
    _console << oss.str() << std::endl;
}

void
ActionWarehouse::executeAllActions()
{
  if (_show_actions)
  {
    _console << "[DBG][ACT] Action Dependency Sets:\n";
    printActionDependencySets();

    _console << "\n[DBG][ACT] Executing actions:" << std::endl;
  }

  for (const auto & task : _ordered_names)
    executeActionsWithAction(task);
}

void
ActionWarehouse::executeActionsWithAction(const std::string & task)
{
  // Set the current task name
  _current_task = task;

  for (ActionIterator act_iter = actionBlocksWithActionBegin(task);
       act_iter != actionBlocksWithActionEnd(task);
       ++act_iter)
  {
    if (_show_actions)
    {
      _console << "[DBG][ACT] "
               << "TASK (" << COLOR_YELLOW << std::setw (24) << task << COLOR_DEFAULT << ") "
               << "TYPE (" << COLOR_YELLOW << std::setw (32) << (*act_iter)->type() << COLOR_DEFAULT << ") "
               << "NAME (" << COLOR_YELLOW << std::setw (16) << (*act_iter)->name() << COLOR_DEFAULT << ") \n";

      (*act_iter)->act();
    }
    else
      (*act_iter)->act();
  }
}

void
ActionWarehouse::printInputFile(std::ostream & out)
{
  InputFileFormatter tree(false);

  std::map<std::string, std::vector<Action *> >::iterator iter;

  std::vector<Action *> ordered_actions;
  for (const auto & block : _action_blocks)
    for (const auto & act : block.second)
      ordered_actions.push_back(act);

  for (const auto & act : ordered_actions)
  {
    std::string name;
    if (act->isParamValid("parser_syntax"))
      name = act->getParam<std::string>("parser_syntax");
    else
      name = act->name();
    const std::set<std::string> & tasks = act->getAllTasks();
    mooseAssert(!tasks.empty(), "Task list is empty");

    bool is_parent;
    if (_syntax.isAssociated(name, &is_parent) != "")
     {
      InputParameters params = act->parameters();

      // TODO: Do we need to insert more nodes for each task?
      tree.insertNode(name, *tasks.begin(), true, &params);

      MooseObjectAction * moose_object_action = dynamic_cast<MooseObjectAction *>(act);
      if (moose_object_action)
       {
        InputParameters obj_params = moose_object_action->getObjectParams();
        tree.insertNode(name, *tasks.begin(), false, &obj_params);
       }
     }
  }

  out << tree.print("");
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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 <unwindstack/MachineArm.h>
#include <unwindstack/MachineArm64.h>
#include <unwindstack/MachineMips.h>
#include <unwindstack/MachineMips64.h>
#include <unwindstack/MachineX86.h>
#include <unwindstack/MachineX86_64.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/RegsArm.h>
#include <unwindstack/RegsArm64.h>
#include <unwindstack/RegsMips.h>
#include <unwindstack/RegsMips64.h>
#include <unwindstack/RegsX86.h>
#include <unwindstack/RegsX86_64.h>
#include <unwindstack/Unwinder.h>
#include <unwindstack/UserArm.h>
#include <unwindstack/UserArm64.h>
#include <unwindstack/UserMips.h>
#include <unwindstack/UserMips64.h>
#include <unwindstack/UserX86.h>
#include <unwindstack/UserX86_64.h>

#include <procinfo/process_map.h>

#include "perfetto/base/file_utils.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/scoped_file.h"
#include "src/profiling/memory/unwinding.h"
#include "src/profiling/memory/wire_protocol.h"

namespace perfetto {

namespace {

size_t kMaxFrames = 1000;

std::unique_ptr<unwindstack::Regs> CreateFromRawData(unwindstack::ArchEnum arch,
                                                     void* raw_data) {
  std::unique_ptr<unwindstack::Regs> ret;
  // unwindstack::RegsX::Read returns a raw ptr which we are expected to free.
  switch (arch) {
    case unwindstack::ARCH_X86:
      ret.reset(unwindstack::RegsX86::Read(raw_data));
      break;
    case unwindstack::ARCH_X86_64:
      ret.reset(unwindstack::RegsX86_64::Read(raw_data));
      break;
    case unwindstack::ARCH_ARM:
      ret.reset(unwindstack::RegsArm::Read(raw_data));
      break;
    case unwindstack::ARCH_ARM64:
      ret.reset(unwindstack::RegsArm64::Read(raw_data));
      break;
    case unwindstack::ARCH_MIPS:
      ret.reset(unwindstack::RegsMips::Read(raw_data));
      break;
    case unwindstack::ARCH_MIPS64:
      ret.reset(unwindstack::RegsMips64::Read(raw_data));
      break;
    case unwindstack::ARCH_UNKNOWN:
      ret.reset(nullptr);
      break;
  }
  return ret;
}

}  // namespace

size_t RegSize(unwindstack::ArchEnum arch) {
  switch (arch) {
    case unwindstack::ARCH_X86:
      return unwindstack::X86_REG_LAST * sizeof(uint32_t);
    case unwindstack::ARCH_X86_64:
      return unwindstack::X86_64_REG_LAST * sizeof(uint64_t);
    case unwindstack::ARCH_ARM:
      return unwindstack::ARM_REG_LAST * sizeof(uint32_t);
    case unwindstack::ARCH_ARM64:
      return unwindstack::ARM64_REG_LAST * sizeof(uint64_t);
    case unwindstack::ARCH_MIPS:
      return unwindstack::MIPS_REG_LAST * sizeof(uint32_t);
    case unwindstack::ARCH_MIPS64:
      return unwindstack::MIPS64_REG_LAST * sizeof(uint64_t);
    case unwindstack::ARCH_UNKNOWN:
      PERFETTO_DCHECK(false);
      return 0;
  }
}

StackMemory::StackMemory(int mem_fd, uint64_t sp, uint8_t* stack, size_t size)
    : mem_fd_(mem_fd), sp_(sp), stack_end_(sp + size), stack_(stack) {}

size_t StackMemory::Read(uint64_t addr, void* dst, size_t size) {
  if (addr >= sp_ && addr + size <= stack_end_ && addr + size > sp_) {
    size_t offset = static_cast<size_t>(addr - sp_);
    memcpy(dst, stack_ + offset, size);
    return size;
  }

  if (lseek(mem_fd_, static_cast<off_t>(addr), SEEK_SET) == -1)
    return 0;

  ssize_t rd = read(mem_fd_, dst, size);
  if (rd == -1) {
    PERFETTO_DPLOG("read");
    return 0;
  }
  return static_cast<size_t>(rd);
}

FileDescriptorMaps::FileDescriptorMaps(base::ScopedFile fd)
    : fd_(std::move(fd)) {}

bool FileDescriptorMaps::Parse() {
  // If the process has already exited, lseek or ReadFileDescriptor will
  // return false.
  if (lseek(*fd_, 0, SEEK_SET) == -1)
    return false;

  std::string content;
  if (!base::ReadFileDescriptor(*fd_, &content))
    return false;
  return android::procinfo::ReadMapFileContent(
      &content[0], [&](uint64_t start, uint64_t end, uint16_t flags,
                       uint64_t pgoff, const char* name) {
        // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
        if (strncmp(name, "/dev/", 5) == 0 &&
            strncmp(name + 5, "ashmem/", 7) != 0) {
          flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
        }
        maps_.push_back(
            new unwindstack::MapInfo(start, end, pgoff, flags, name));
      });
}

void FileDescriptorMaps::Reset() {
  for (unwindstack::MapInfo* info : maps_)
    delete info;
  maps_.clear();
}

bool DoUnwind(WireMessage* msg, ProcessMetadata* metadata, AllocRecord* out) {
  AllocMetadata* alloc_metadata = msg->alloc_header;
  std::unique_ptr<unwindstack::Regs> regs(
      CreateFromRawData(alloc_metadata->arch, alloc_metadata->register_data));
  if (regs == nullptr) {
    PERFETTO_ELOG("regs");
    return false;
  }
  out->alloc_metadata = *alloc_metadata;
  uint8_t* stack = reinterpret_cast<uint8_t*>(msg->payload);
  std::shared_ptr<unwindstack::Memory> mems = std::make_shared<StackMemory>(
      *metadata->mem_fd, alloc_metadata->stack_pointer, stack,
      msg->payload_size);
  unwindstack::Unwinder unwinder(kMaxFrames, &metadata->maps, regs.get(), mems);
  // Surpress incorrect "variable may be uninitialized" error for if condition
  // after this loop. error_code = LastErrorCode gets run at least once.
  uint8_t error_code = 0;
  for (int attempt = 0; attempt < 2; ++attempt) {
    if (attempt > 0) {
      metadata->maps.Reset();
      metadata->maps.Parse();
    }
    unwinder.Unwind();
    error_code = unwinder.LastErrorCode();
    if (error_code != unwindstack::ERROR_INVALID_MAP)
      break;
  }
  if (error_code == 0)
    out->frames = unwinder.frames();
  else
    PERFETTO_DLOG("unwinding failed %" PRIu8, error_code);
  return error_code == 0;
}

bool HandleUnwindingRecord(UnwindingRecord* rec, BookkeepingRecord* out) {
  WireMessage msg;
  if (!ReceiveWireMessage(reinterpret_cast<char*>(rec->data.get()), rec->size,
                          &msg))
    return false;
  switch (msg.record_type) {
    case RecordType::Malloc: {
      std::shared_ptr<ProcessMetadata> metadata = rec->metadata.lock();
      if (!metadata)
        // Process has already gone away.
        return false;

      out->metadata = std::move(rec->metadata);
      out->free_record = {};
      return DoUnwind(&msg, metadata.get(), &out->alloc_record);
    }
    case RecordType::Free: {
      // We need to keep this alive, because msg.free_header is a pointer into
      // this.
      out->metadata = std::move(rec->metadata);
      out->free_record.free_data = std::move(rec->data);
      out->free_record.metadata = msg.free_header;
      out->alloc_record = {};
      return true;
    }
  }
}

__attribute__((noreturn)) void UnwindingMainLoop(
    BoundedQueue<UnwindingRecord>* input_queue,
    BoundedQueue<BookkeepingRecord>* output_queue) {
  for (;;) {
    UnwindingRecord rec = input_queue->Get();
    BookkeepingRecord out;
    if (HandleUnwindingRecord(&rec, &out))
      output_queue->Add(std::move(out));
  }
}

void HandleBookkeepingRecord(BookkeepingRecord* rec) {
  std::shared_ptr<ProcessMetadata> metadata = rec->metadata.lock();
  if (!metadata)
    // Process has already gone away.
    return;

  if (rec->free_record.free_data) {
    FreeRecord& free_rec = rec->free_record;
    FreePageEntry* entries = free_rec.metadata->entries;
    uint64_t num_entries = free_rec.metadata->num_entries;
    if (num_entries > kFreePageSize)
      return;
    for (size_t i = 0; i < num_entries; ++i) {
      const FreePageEntry& entry = entries[i];
      metadata->heap_dump.RecordFree(entry.addr, entry.sequence_number);
    }
  } else {
    AllocRecord& alloc_rec = rec->alloc_record;
    std::vector<CodeLocation> code_locations;
    for (unwindstack::FrameData& frame : alloc_rec.frames)
      code_locations.emplace_back(frame.map_name, frame.function_name);
    metadata->heap_dump.RecordMalloc(code_locations,
                                     alloc_rec.alloc_metadata.alloc_address,
                                     alloc_rec.alloc_metadata.alloc_size,
                                     alloc_rec.alloc_metadata.sequence_number);
  }
}

__attribute__((noreturn)) void BookkeepingMainLoop(
    BoundedQueue<BookkeepingRecord>* input_queue) {
  for (;;) {
    BookkeepingRecord rec = input_queue->Get();
    HandleBookkeepingRecord(&rec);
  }
}

}  // namespace perfetto
<commit_msg>Add new parameter for creation of MapInfo object.<commit_after>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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 <unwindstack/MachineArm.h>
#include <unwindstack/MachineArm64.h>
#include <unwindstack/MachineMips.h>
#include <unwindstack/MachineMips64.h>
#include <unwindstack/MachineX86.h>
#include <unwindstack/MachineX86_64.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/RegsArm.h>
#include <unwindstack/RegsArm64.h>
#include <unwindstack/RegsMips.h>
#include <unwindstack/RegsMips64.h>
#include <unwindstack/RegsX86.h>
#include <unwindstack/RegsX86_64.h>
#include <unwindstack/Unwinder.h>
#include <unwindstack/UserArm.h>
#include <unwindstack/UserArm64.h>
#include <unwindstack/UserMips.h>
#include <unwindstack/UserMips64.h>
#include <unwindstack/UserX86.h>
#include <unwindstack/UserX86_64.h>

#include <procinfo/process_map.h>

#include "perfetto/base/file_utils.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/scoped_file.h"
#include "src/profiling/memory/unwinding.h"
#include "src/profiling/memory/wire_protocol.h"

namespace perfetto {

namespace {

size_t kMaxFrames = 1000;

std::unique_ptr<unwindstack::Regs> CreateFromRawData(unwindstack::ArchEnum arch,
                                                     void* raw_data) {
  std::unique_ptr<unwindstack::Regs> ret;
  // unwindstack::RegsX::Read returns a raw ptr which we are expected to free.
  switch (arch) {
    case unwindstack::ARCH_X86:
      ret.reset(unwindstack::RegsX86::Read(raw_data));
      break;
    case unwindstack::ARCH_X86_64:
      ret.reset(unwindstack::RegsX86_64::Read(raw_data));
      break;
    case unwindstack::ARCH_ARM:
      ret.reset(unwindstack::RegsArm::Read(raw_data));
      break;
    case unwindstack::ARCH_ARM64:
      ret.reset(unwindstack::RegsArm64::Read(raw_data));
      break;
    case unwindstack::ARCH_MIPS:
      ret.reset(unwindstack::RegsMips::Read(raw_data));
      break;
    case unwindstack::ARCH_MIPS64:
      ret.reset(unwindstack::RegsMips64::Read(raw_data));
      break;
    case unwindstack::ARCH_UNKNOWN:
      ret.reset(nullptr);
      break;
  }
  return ret;
}

}  // namespace

size_t RegSize(unwindstack::ArchEnum arch) {
  switch (arch) {
    case unwindstack::ARCH_X86:
      return unwindstack::X86_REG_LAST * sizeof(uint32_t);
    case unwindstack::ARCH_X86_64:
      return unwindstack::X86_64_REG_LAST * sizeof(uint64_t);
    case unwindstack::ARCH_ARM:
      return unwindstack::ARM_REG_LAST * sizeof(uint32_t);
    case unwindstack::ARCH_ARM64:
      return unwindstack::ARM64_REG_LAST * sizeof(uint64_t);
    case unwindstack::ARCH_MIPS:
      return unwindstack::MIPS_REG_LAST * sizeof(uint32_t);
    case unwindstack::ARCH_MIPS64:
      return unwindstack::MIPS64_REG_LAST * sizeof(uint64_t);
    case unwindstack::ARCH_UNKNOWN:
      PERFETTO_DCHECK(false);
      return 0;
  }
}

StackMemory::StackMemory(int mem_fd, uint64_t sp, uint8_t* stack, size_t size)
    : mem_fd_(mem_fd), sp_(sp), stack_end_(sp + size), stack_(stack) {}

size_t StackMemory::Read(uint64_t addr, void* dst, size_t size) {
  if (addr >= sp_ && addr + size <= stack_end_ && addr + size > sp_) {
    size_t offset = static_cast<size_t>(addr - sp_);
    memcpy(dst, stack_ + offset, size);
    return size;
  }

  if (lseek(mem_fd_, static_cast<off_t>(addr), SEEK_SET) == -1)
    return 0;

  ssize_t rd = read(mem_fd_, dst, size);
  if (rd == -1) {
    PERFETTO_DPLOG("read");
    return 0;
  }
  return static_cast<size_t>(rd);
}

FileDescriptorMaps::FileDescriptorMaps(base::ScopedFile fd)
    : fd_(std::move(fd)) {}

bool FileDescriptorMaps::Parse() {
  // If the process has already exited, lseek or ReadFileDescriptor will
  // return false.
  if (lseek(*fd_, 0, SEEK_SET) == -1)
    return false;

  std::string content;
  if (!base::ReadFileDescriptor(*fd_, &content))
    return false;
  return android::procinfo::ReadMapFileContent(
      &content[0], [&](uint64_t start, uint64_t end, uint16_t flags,
                       uint64_t pgoff, const char* name) {
        // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
        if (strncmp(name, "/dev/", 5) == 0 &&
            strncmp(name + 5, "ashmem/", 7) != 0) {
          flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
        }
        maps_.push_back(
            new unwindstack::MapInfo(nullptr, start, end, pgoff, flags, name));
      });
}

void FileDescriptorMaps::Reset() {
  for (unwindstack::MapInfo* info : maps_)
    delete info;
  maps_.clear();
}

bool DoUnwind(WireMessage* msg, ProcessMetadata* metadata, AllocRecord* out) {
  AllocMetadata* alloc_metadata = msg->alloc_header;
  std::unique_ptr<unwindstack::Regs> regs(
      CreateFromRawData(alloc_metadata->arch, alloc_metadata->register_data));
  if (regs == nullptr) {
    PERFETTO_ELOG("regs");
    return false;
  }
  out->alloc_metadata = *alloc_metadata;
  uint8_t* stack = reinterpret_cast<uint8_t*>(msg->payload);
  std::shared_ptr<unwindstack::Memory> mems = std::make_shared<StackMemory>(
      *metadata->mem_fd, alloc_metadata->stack_pointer, stack,
      msg->payload_size);
  unwindstack::Unwinder unwinder(kMaxFrames, &metadata->maps, regs.get(), mems);
  // Surpress incorrect "variable may be uninitialized" error for if condition
  // after this loop. error_code = LastErrorCode gets run at least once.
  uint8_t error_code = 0;
  for (int attempt = 0; attempt < 2; ++attempt) {
    if (attempt > 0) {
      metadata->maps.Reset();
      metadata->maps.Parse();
    }
    unwinder.Unwind();
    error_code = unwinder.LastErrorCode();
    if (error_code != unwindstack::ERROR_INVALID_MAP)
      break;
  }
  if (error_code == 0)
    out->frames = unwinder.frames();
  else
    PERFETTO_DLOG("unwinding failed %" PRIu8, error_code);
  return error_code == 0;
}

bool HandleUnwindingRecord(UnwindingRecord* rec, BookkeepingRecord* out) {
  WireMessage msg;
  if (!ReceiveWireMessage(reinterpret_cast<char*>(rec->data.get()), rec->size,
                          &msg))
    return false;
  switch (msg.record_type) {
    case RecordType::Malloc: {
      std::shared_ptr<ProcessMetadata> metadata = rec->metadata.lock();
      if (!metadata)
        // Process has already gone away.
        return false;

      out->metadata = std::move(rec->metadata);
      out->free_record = {};
      return DoUnwind(&msg, metadata.get(), &out->alloc_record);
    }
    case RecordType::Free: {
      // We need to keep this alive, because msg.free_header is a pointer into
      // this.
      out->metadata = std::move(rec->metadata);
      out->free_record.free_data = std::move(rec->data);
      out->free_record.metadata = msg.free_header;
      out->alloc_record = {};
      return true;
    }
  }
}

__attribute__((noreturn)) void UnwindingMainLoop(
    BoundedQueue<UnwindingRecord>* input_queue,
    BoundedQueue<BookkeepingRecord>* output_queue) {
  for (;;) {
    UnwindingRecord rec = input_queue->Get();
    BookkeepingRecord out;
    if (HandleUnwindingRecord(&rec, &out))
      output_queue->Add(std::move(out));
  }
}

void HandleBookkeepingRecord(BookkeepingRecord* rec) {
  std::shared_ptr<ProcessMetadata> metadata = rec->metadata.lock();
  if (!metadata)
    // Process has already gone away.
    return;

  if (rec->free_record.free_data) {
    FreeRecord& free_rec = rec->free_record;
    FreePageEntry* entries = free_rec.metadata->entries;
    uint64_t num_entries = free_rec.metadata->num_entries;
    if (num_entries > kFreePageSize)
      return;
    for (size_t i = 0; i < num_entries; ++i) {
      const FreePageEntry& entry = entries[i];
      metadata->heap_dump.RecordFree(entry.addr, entry.sequence_number);
    }
  } else {
    AllocRecord& alloc_rec = rec->alloc_record;
    std::vector<CodeLocation> code_locations;
    for (unwindstack::FrameData& frame : alloc_rec.frames)
      code_locations.emplace_back(frame.map_name, frame.function_name);
    metadata->heap_dump.RecordMalloc(code_locations,
                                     alloc_rec.alloc_metadata.alloc_address,
                                     alloc_rec.alloc_metadata.alloc_size,
                                     alloc_rec.alloc_metadata.sequence_number);
  }
}

__attribute__((noreturn)) void BookkeepingMainLoop(
    BoundedQueue<BookkeepingRecord>* input_queue) {
  for (;;) {
    BookkeepingRecord rec = input_queue->Get();
    HandleBookkeepingRecord(&rec);
  }
}

}  // namespace perfetto
<|endoftext|>


<commit_before>/*
 *  Copyright 2008-2009 NVIDIA Corporation
 *
 *  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 <cusp/detail/device/generalized_spmv/coo_flat.h>

#include <cusp/array1d.h>
#include <cusp/exception.h>
#include <cusp/coo_matrix.h>
#include <cusp/csr_matrix.h>

#include <cusp/print.h>

#include <thrust/count.h>
#include <thrust/transform.h>
#include <thrust/transform_scan.h>
#include <thrust/iterator/constant_iterator.h>
#include <thrust/iterator/counting_iterator.h>
#include <thrust/iterator/zip_iterator.h>

namespace cusp
{
namespace graph
{
namespace detail
{

// http://burtleburtle.net/bob/hash/integer.html
struct simple_hash
{
    __host__ __device__
    unsigned int operator()(unsigned int a)
    {
        a = (a + 0x7ed55d16) + (a << 12);
        a = (a ^ 0xc761c23c) ^ (a >> 19);
        a = (a + 0x165667b1) + (a <<  5);
        a = (a + 0xd3a2646c) ^ (a <<  9);
        a = (a + 0xfd7046c5) + (a <<  3);
        a = (a ^ 0xb55a4f09) ^ (a >> 16);
        return a;
    }
};

struct process_mis_nodes
{
    template <typename Tuple>
    __host__ __device__
    void operator()(Tuple t)
    {
        if (thrust::get<1>(t) == 1)                     // undecided node
        {
            if (thrust::get<0>(t) == thrust::get<3>(t)) // i == maximal_index
              thrust::get<1>(t) = 2;                    // mis_node
        }
    }
};

struct process_non_mis_nodes
{
    template <typename Tuple>
    __host__ __device__
    void operator()(Tuple t)
    {
        if (thrust::get<0>(t) == 1)            // undecided node
        {
            if (thrust::get<1>(t) == 2)        // maximal_state == mis_node
              thrust::get<0>(t) = 0;           // non_mis_node
        }
    }
};

template <typename Matrix, typename IndexType>
void propagate_distances(const Matrix& A,
                         const IndexType i,
                         const size_t d,
                         const size_t k,
                         cusp::array1d<size_t,cusp::host_memory>& distance)
{
  distance[i] = d;

  if (d < k)
  {
    for(IndexType jj = A.row_offsets[i]; jj < A.row_offsets[i + 1]; jj++)
    {
      IndexType j = A.column_indices[jj];

      // update only if necessary
      if (d + 1 < distance[j])
        propagate_distances(A, j, d + 1, k, distance);
    }
  }
}

template <typename NodeStateType>
struct is_active_edge
{
  template <typename Tuple>
  __host__ __device__
  bool operator()(const Tuple& t) const
  {
    return thrust::get<0>(t) == 1 && thrust::get<1>(t) == 1;
  }
};

template <typename NodeStateType>
struct is_active_node
{
  __host__ __device__
  bool operator()(const NodeStateType& s) const
  {
    return s == 1;
  }
};

////////////////
// Host Paths //
////////////////

template <typename Matrix, typename ArrayType>
size_t maximal_independent_set(const Matrix& A, ArrayType& stencil, size_t k,
                               cusp::csr_format, cusp::host_memory)
{
  typedef typename Matrix::index_type   IndexType;

  const IndexType N = A.num_rows;

  // distance to nearest MIS node
  cusp::array1d<size_t,cusp::host_memory> distance(N, k + 1);

  // count number of MIS nodes
  size_t set_nodes = 0;

  // pick MIS-k nodes greedily and deactivate all their k-neighbors
  for(IndexType i = 0; i < N; i++)
  {
    if (distance[i] > k)
    {
      set_nodes++;

      // reset distances on all k-ring neighbors
      propagate_distances(A, i, 0, k, distance);
    }
  }

  // write output
  stencil.resize(N);

  for (IndexType i = 0; i < N; i++)
      stencil[i] = distance[i] == 0;

  return set_nodes;
}

//////////////////
// Device Paths //
//////////////////

template <typename Matrix, typename ArrayType>
size_t maximal_independent_set(const Matrix& A, ArrayType& stencil, size_t k,
                               cusp::coo_format, cusp::device_memory)
{
    typedef typename Matrix::index_type   IndexType;
    typedef typename Matrix::value_type   ValueType;
    typedef typename Matrix::memory_space MemorySpace;
    typedef unsigned int RandomType;
    typedef unsigned int NodeStateType;

    const IndexType N = A.num_rows;

    cusp::array1d<RandomType,MemorySpace> random_values(N);
    thrust::transform(thrust::counting_iterator<IndexType>(0), thrust::counting_iterator<IndexType>(N),
                      random_values.begin(),
                      simple_hash());

    cusp::array1d<NodeStateType,MemorySpace> states(N, 1);

    typedef typename thrust::tuple<NodeStateType,RandomType,IndexType> Tuple;

    cusp::array1d<NodeStateType,MemorySpace> maximal_states(N);
    cusp::array1d<RandomType,MemorySpace>    maximal_values(N);
    cusp::array1d<IndexType,MemorySpace>     maximal_indices(N);

    cusp::array1d<NodeStateType,MemorySpace> last_states(maximal_states);
    cusp::array1d<RandomType,MemorySpace>    last_values(maximal_values);
    cusp::array1d<IndexType,MemorySpace>     last_indices(maximal_indices);

    // TODO choose threshold in a more principled manner
    size_t compaction_threshold = 0; // (N < 10000) ? 0 : (N / 10);
    size_t active_nodes = N;

    do
    {
        // find the largest (state,value,index) 1-ring neighbor for each node
        cusp::detail::device::cuda::spmv_coo
            (A.num_rows, A.num_entries,
             A.row_indices.begin(), A.column_indices.begin(), thrust::constant_iterator<int>(1),  // XXX should we mask explicit zeros? (e.g. DIA, array2d)
             thrust::make_zip_iterator(thrust::make_tuple(states.begin(), random_values.begin(), thrust::counting_iterator<IndexType>(0))),
             thrust::make_zip_iterator(thrust::make_tuple(states.begin(), random_values.begin(), thrust::counting_iterator<IndexType>(0))),
             thrust::make_zip_iterator(thrust::make_tuple(maximal_states.begin(), maximal_values.begin(), maximal_indices.begin())),
             thrust::project2nd<Tuple,Tuple>(), thrust::maximum<Tuple>());

        // find the largest (state,value,index) k-ring neighbor for each node (if k > 1)
        for(size_t ring = 1; ring < k; ring++)
        {
            last_states.resize (N); last_states.swap (maximal_states);
            last_values.resize (N); last_values.swap (maximal_values);
            last_indices.resize(N); last_indices.swap(maximal_indices);

            // TODO replace with call to generalized method
            cusp::detail::device::cuda::spmv_coo
                (A.num_rows, A.num_entries,
                 A.row_indices.begin(), A.column_indices.begin(), thrust::constant_iterator<int>(1),  // XXX should we mask explicit zeros? (e.g. DIA, array2d)
                 thrust::make_zip_iterator(thrust::make_tuple(last_states.begin(), last_values.begin(), last_indices.begin())),
                 thrust::make_zip_iterator(thrust::make_tuple(last_states.begin(), last_values.begin(), last_indices.begin())),
                 thrust::make_zip_iterator(thrust::make_tuple(maximal_states.begin(), maximal_values.begin(), maximal_indices.begin())),
                 thrust::project2nd<Tuple,Tuple>(), thrust::maximum<Tuple>());
        }

        // label local maxima as MIS nodes
        thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(thrust::counting_iterator<IndexType>(0), states.begin(), maximal_states.begin(), maximal_indices.begin())),
                         thrust::make_zip_iterator(thrust::make_tuple(thrust::counting_iterator<IndexType>(0), states.begin(), maximal_states.begin(), maximal_indices.begin())) + N,
                         process_mis_nodes());

        // label k-ring neighbors of MIS nodes as non-MIS nodes
        thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(states.begin(), thrust::make_permutation_iterator(states.begin(), maximal_indices.begin()))),
                         thrust::make_zip_iterator(thrust::make_tuple(states.begin(), thrust::make_permutation_iterator(states.begin(), maximal_indices.begin()))) + N,
                         process_non_mis_nodes());

        active_nodes = thrust::count(states.begin(), states.end(), 1);
    } while (active_nodes > compaction_threshold);

    // resize output
    stencil.resize(N);

    // mark all mis nodes
    thrust::transform(states.begin(), states.end(), thrust::constant_iterator<NodeStateType>(2), stencil.begin(), thrust::equal_to<NodeStateType>());

    // return the size of the MIS
    return thrust::count(stencil.begin(), stencil.end(), typename ArrayType::value_type(true));
}

//////////////////
// General Path //
//////////////////

template <typename Matrix, typename ArrayType,
          typename Format, typename MemorySpace>
size_t maximal_independent_set(const Matrix& A, ArrayType& stencil, size_t k,
                               Format, MemorySpace)
{
  typedef typename Matrix::index_type   IndexType;
  typedef typename Matrix::value_type   ValueType;

  // convert matrix to CSR format and compute on the host
  cusp::csr_matrix<IndexType,ValueType,cusp::host_memory> A_csr(A);

  return cusp::graph::maximal_independent_set(A_csr, stencil, k);
}

} // end namespace detail

/////////////////
// Entry Point //
/////////////////

template <typename Matrix, typename ArrayType>
size_t maximal_independent_set(const Matrix& A, ArrayType& stencil, size_t k)
{
    CUSP_PROFILE_SCOPED();

    if(A.num_rows != A.num_cols)
        throw cusp::invalid_input_exception("matrix must be square");

    if (k == 0)
    {
        stencil.resize(A.num_rows);
        thrust::fill(stencil.begin(), stencil.end(), typename ArrayType::value_type(1));
        return stencil.size();
    }
    else
    {
        return cusp::graph::detail::maximal_independent_set(A, stencil, k, typename Matrix::format(), typename Matrix::memory_space());
    }
}

} // end namespace graph
} // end namespace cusp

<commit_msg>minor improvements to device MIS code<commit_after>/*
 *  Copyright 2008-2009 NVIDIA Corporation
 *
 *  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 <cusp/detail/device/generalized_spmv/coo_flat.h>

#include <cusp/array1d.h>
#include <cusp/exception.h>
#include <cusp/coo_matrix.h>
#include <cusp/csr_matrix.h>

#include <cusp/print.h>

#include <thrust/count.h>
#include <thrust/transform.h>
#include <thrust/transform_scan.h>
#include <thrust/iterator/constant_iterator.h>
#include <thrust/iterator/counting_iterator.h>
#include <thrust/iterator/zip_iterator.h>

namespace cusp
{
namespace graph
{
namespace detail
{

// http://burtleburtle.net/bob/hash/integer.html
struct simple_hash
{
    __host__ __device__
    unsigned int operator()(unsigned int a)
    {
        a = (a + 0x7ed55d16) + (a << 12);
        a = (a ^ 0xc761c23c) ^ (a >> 19);
        a = (a + 0x165667b1) + (a <<  5);
        a = (a + 0xd3a2646c) ^ (a <<  9);
        a = (a + 0xfd7046c5) + (a <<  3);
        a = (a ^ 0xb55a4f09) ^ (a >> 16);
        return a;
    }
};

struct process_mis_nodes
{
    template <typename Tuple>
    __host__ __device__
    void operator()(Tuple t)
    {
        if (thrust::get<1>(t) == 1)                     // undecided node
        {
            if (thrust::get<0>(t) == thrust::get<3>(t)) // i == maximal_index
              thrust::get<1>(t) = 2;                    // mis_node
        }
    }
};

struct process_non_mis_nodes
{
    template <typename Tuple>
    __host__ __device__
    void operator()(Tuple t)
    {
        if (thrust::get<0>(t) == 1)            // undecided node
        {
            if (thrust::get<1>(t) == 2)        // maximal_state == mis_node
              thrust::get<0>(t) = 0;           // non_mis_node
        }
    }
};

template <typename Matrix, typename IndexType>
void propagate_distances(const Matrix& A,
                         const IndexType i,
                         const size_t d,
                         const size_t k,
                         cusp::array1d<size_t,cusp::host_memory>& distance)
{
  distance[i] = d;

  if (d < k)
  {
    for(IndexType jj = A.row_offsets[i]; jj < A.row_offsets[i + 1]; jj++)
    {
      IndexType j = A.column_indices[jj];

      // update only if necessary
      if (d + 1 < distance[j])
        propagate_distances(A, j, d + 1, k, distance);
    }
  }
}

template <typename NodeStateType>
struct is_active_edge
{
  template <typename Tuple>
  __host__ __device__
  bool operator()(const Tuple& t) const
  {
    return thrust::get<0>(t) == 1 && thrust::get<1>(t) == 1;
  }
};

template <typename NodeStateType>
struct is_active_node
{
  __host__ __device__
  bool operator()(const NodeStateType& s) const
  {
    return s == 1;
  }
};

////////////////
// Host Paths //
////////////////

template <typename Matrix, typename ArrayType>
size_t maximal_independent_set(const Matrix& A, ArrayType& stencil, size_t k,
                               cusp::csr_format, cusp::host_memory)
{
  typedef typename Matrix::index_type   IndexType;

  const IndexType N = A.num_rows;

  // distance to nearest MIS node
  cusp::array1d<size_t,cusp::host_memory> distance(N, k + 1);

  // count number of MIS nodes
  size_t set_nodes = 0;

  // pick MIS-k nodes greedily and deactivate all their k-neighbors
  for(IndexType i = 0; i < N; i++)
  {
    if (distance[i] > k)
    {
      set_nodes++;

      // reset distances on all k-ring neighbors
      propagate_distances(A, i, 0, k, distance);
    }
  }

  // write output
  stencil.resize(N);

  for (IndexType i = 0; i < N; i++)
      stencil[i] = distance[i] == 0;

  return set_nodes;
}

//////////////////
// Device Paths //
//////////////////

template <typename Matrix, typename ArrayType>
size_t maximal_independent_set(const Matrix& A, ArrayType& stencil, size_t k,
                               cusp::coo_format, cusp::device_memory)
{
    typedef typename Matrix::index_type   IndexType;
    typedef typename Matrix::value_type   ValueType;
    typedef typename Matrix::memory_space MemorySpace;
    typedef unsigned int  RandomType;
    typedef unsigned char NodeStateType;

    const IndexType N = A.num_rows;

    cusp::array1d<RandomType,MemorySpace> random_values(N);
    thrust::transform(thrust::counting_iterator<IndexType>(0), thrust::counting_iterator<IndexType>(N),
                      random_values.begin(),
                      simple_hash());

    cusp::array1d<NodeStateType,MemorySpace> states(N, 1);

    typedef typename thrust::tuple<NodeStateType,RandomType,IndexType> Tuple;

    cusp::array1d<NodeStateType,MemorySpace> maximal_states(N);
    cusp::array1d<RandomType,MemorySpace>    maximal_values(N);
    cusp::array1d<IndexType,MemorySpace>     maximal_indices(N);

    cusp::array1d<NodeStateType,MemorySpace> last_states;
    cusp::array1d<RandomType,MemorySpace>    last_values;
    cusp::array1d<IndexType,MemorySpace>     last_indices;;

    // TODO choose threshold in a more principled manner
    size_t compaction_threshold = 0; // (N < 10000) ? 0 : (N / 10);
    size_t active_nodes = N;

    do
    {
        // find the largest (state,value,index) 1-ring neighbor for each node
        cusp::detail::device::cuda::spmv_coo
            (A.num_rows, A.num_entries,
             A.row_indices.begin(), A.column_indices.begin(), thrust::constant_iterator<Tuple>(Tuple(0,0)),  // XXX should we mask explicit zeros? (e.g. DIA, array2d)
             thrust::make_zip_iterator(thrust::make_tuple(states.begin(), random_values.begin(), thrust::counting_iterator<IndexType>(0))),
             thrust::make_zip_iterator(thrust::make_tuple(states.begin(), random_values.begin(), thrust::counting_iterator<IndexType>(0))),
             thrust::make_zip_iterator(thrust::make_tuple(maximal_states.begin(), maximal_values.begin(), maximal_indices.begin())),
             thrust::project2nd<Tuple,Tuple>(), thrust::maximum<Tuple>());

        // find the largest (state,value,index) k-ring neighbor for each node (if k > 1)
        for(size_t ring = 1; ring < k; ring++)
        {
            last_states.resize (N); last_states.swap (maximal_states);
            last_values.resize (N); last_values.swap (maximal_values);
            last_indices.resize(N); last_indices.swap(maximal_indices);

            // TODO replace with call to generalized method
            cusp::detail::device::cuda::spmv_coo
                (A.num_rows, A.num_entries,
                 A.row_indices.begin(), A.column_indices.begin(), thrust::constant_iterator<Tuple>(Tuple(0,0)),  // XXX should we mask explicit zeros? (e.g. DIA, array2d)
                 thrust::make_zip_iterator(thrust::make_tuple(last_states.begin(), last_values.begin(), last_indices.begin())),
                 thrust::make_zip_iterator(thrust::make_tuple(last_states.begin(), last_values.begin(), last_indices.begin())),
                 thrust::make_zip_iterator(thrust::make_tuple(maximal_states.begin(), maximal_values.begin(), maximal_indices.begin())),
                 thrust::project2nd<Tuple,Tuple>(), thrust::maximum<Tuple>());
        }

        // label local maxima as MIS nodes
        thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(thrust::counting_iterator<IndexType>(0), states.begin(), maximal_states.begin(), maximal_indices.begin())),
                         thrust::make_zip_iterator(thrust::make_tuple(thrust::counting_iterator<IndexType>(0), states.begin(), maximal_states.begin(), maximal_indices.begin())) + N,
                         process_mis_nodes());

        // label k-ring neighbors of MIS nodes as non-MIS nodes
        thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(states.begin(), thrust::make_permutation_iterator(states.begin(), maximal_indices.begin()))),
                         thrust::make_zip_iterator(thrust::make_tuple(states.begin(), thrust::make_permutation_iterator(states.begin(), maximal_indices.begin()))) + N,
                         process_non_mis_nodes());

        active_nodes = thrust::count(states.begin(), states.end(), 1);
    } while (active_nodes > compaction_threshold);

    // resize output
    stencil.resize(N);

    // mark all mis nodes
    thrust::transform(states.begin(), states.end(), thrust::constant_iterator<NodeStateType>(2), stencil.begin(), thrust::equal_to<NodeStateType>());

    // return the size of the MIS
    return thrust::count(stencil.begin(), stencil.end(), typename ArrayType::value_type(true));
}

//////////////////
// General Path //
//////////////////

template <typename Matrix, typename ArrayType,
          typename Format, typename MemorySpace>
size_t maximal_independent_set(const Matrix& A, ArrayType& stencil, size_t k,
                               Format, MemorySpace)
{
  typedef typename Matrix::index_type   IndexType;
  typedef typename Matrix::value_type   ValueType;

  // convert matrix to CSR format and compute on the host
  cusp::csr_matrix<IndexType,ValueType,cusp::host_memory> A_csr(A);

  return cusp::graph::maximal_independent_set(A_csr, stencil, k);
}

} // end namespace detail

/////////////////
// Entry Point //
/////////////////

template <typename Matrix, typename ArrayType>
size_t maximal_independent_set(const Matrix& A, ArrayType& stencil, size_t k)
{
    CUSP_PROFILE_SCOPED();

    if(A.num_rows != A.num_cols)
        throw cusp::invalid_input_exception("matrix must be square");

    if (k == 0)
    {
        stencil.resize(A.num_rows);
        thrust::fill(stencil.begin(), stencil.end(), typename ArrayType::value_type(1));
        return stencil.size();
    }
    else
    {
        return cusp::graph::detail::maximal_independent_set(A, stencil, k, typename Matrix::format(), typename Matrix::memory_space());
    }
}

} // end namespace graph
} // end namespace cusp

<|endoftext|>


<commit_before>/*  USBlinuz
 *  Copyright (C) 2002-2004  USBlinuz Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdlib.h>
#include <string>
#include <errno.h>

#include "qemu-usb/vl.h"
#include "USB.h"
#include "deviceproxy.h"

const unsigned char version  = PS2E_USB_VERSION;
const unsigned char revision = 0;
const unsigned char build    = 5;    // increase that with each version
const unsigned char fix      = 2;

static char libraryName[256];

OHCIState *qemu_ohci = NULL;
USBDevice *usb_device1 = NULL;
USBDevice *usb_device2 = NULL;
bool configChanged = false;

Config conf;
char USBfreezeID[] = "USBqemuW01";
typedef struct {
	char freezeID[11];
	s64 cycles;
	s64 remaining;
	OHCIState t;
	int extraData; // for future expansion with the device state
} USBfreezeData;

u8 *ram = 0;
USBcallback _USBirq;
FILE *usbLog;
int64_t usb_frame_time;
int64_t usb_bit_time;

s64 clocks = 0;
s64 remaining = 0;

#if _WIN32
HWND gsWnd=NULL;
#endif

void USBirq(int cycles)
{
	USB_LOG("USBirq.\n");

	_USBirq(cycles);
}

void __Log(const char *fmt, ...) {
	va_list list;

	if (!conf.Log ||!usbLog) return;

	va_start(list, fmt);
	vfprintf(usbLog, fmt, list);
	va_end(list);
}

//Simpler to reset and reattach after USBclose/USBopen
void Reset()
{
	if(qemu_ohci)
		ohci_reset(qemu_ohci);
}

void DestroyDevices()
{
	//FIXME something throws an null ptr exception?
	if(qemu_ohci && qemu_ohci->rhport[PLAYER_ONE_PORT].port.dev) {
		qemu_ohci->rhport[PLAYER_ONE_PORT].port.dev->handle_destroy(qemu_ohci->rhport[PLAYER_ONE_PORT].port.dev);
		qemu_ohci->rhport[PLAYER_ONE_PORT].port.dev = NULL;
	}
	else if(usb_device1) //maybe redundant
		usb_device1->handle_destroy(usb_device1);

	if(qemu_ohci && qemu_ohci->rhport[PLAYER_TWO_PORT].port.dev) {
		qemu_ohci->rhport[PLAYER_TWO_PORT].port.dev->handle_destroy(qemu_ohci->rhport[PLAYER_TWO_PORT].port.dev);
		qemu_ohci->rhport[PLAYER_TWO_PORT].port.dev = NULL;
	}
	else if(usb_device2)
		usb_device2->handle_destroy(usb_device2);

	usb_device1 = NULL;
	usb_device2 = NULL;
}

void CreateDevices()
{
	DeviceProxyBase *devProxy;
	if(!qemu_ohci) return; //No USBinit yet ie. called from config. dialog
	DestroyDevices();

	if (!conf.Port1.empty())
	{
		devProxy = RegisterDevice::instance().Device(conf.Port1);
		if (devProxy)
			usb_device1 = devProxy->CreateDevice(PLAYER_ONE_PORT);
		else
			SysMessage(TEXT("Device 1: Unknown device type"));
	}

	if (!conf.Port0.empty())
	{
		devProxy = RegisterDevice::instance().Device(conf.Port0);
		if (devProxy)
			usb_device2 = devProxy->CreateDevice(PLAYER_TWO_PORT);
		else
			SysMessage(TEXT("Device 2: Unknown device type"));
	}

	//No need for NULL check. NULL device means detach port.
	qemu_ohci->rhport[PLAYER_ONE_PORT].port.attach(&(qemu_ohci->rhport[PLAYER_ONE_PORT].port), usb_device1);
	qemu_ohci->rhport[PLAYER_TWO_PORT].port.attach(&(qemu_ohci->rhport[PLAYER_TWO_PORT].port), usb_device2);
}

EXPORT_C_(u32) PS2EgetLibType() {
	return PS2E_LT_USB;
}

EXPORT_C_(char*) PS2EgetLibName() {

#ifdef _MSC_VER
	sprintf_s
#else
	sprintf
#endif
	( libraryName, "Qemu USB Driver (Wheel) (" __DATE__ ")"
#ifdef _DEBUG
		" (debug)"
#endif
	);

	return libraryName;
}

EXPORT_C_(u32) PS2EgetLibVersion2(u32 type) {
	return (version<<16) | (revision<<8) | build | (fix << 24);
}

EXPORT_C_(s32) USBinit() {
	OSDebugOut(TEXT("USBinit\n"));
	LoadConfig();

	if (conf.Log)
	{
		usbLog = fopen("logs/usbLog.txt", "w");
		if(usbLog) setvbuf(usbLog, NULL,  _IONBF, 0);
		USB_LOG("usbqemu wheel mod plugin version %d,%d\n",revision,build);
		USB_LOG("USBinit\n");
	}

	qemu_ohci = ohci_create(0x1f801600,2);
	if(!qemu_ohci) return 1;

	return 0;
}

EXPORT_C_(void) USBshutdown() {

	OSDebugOut(TEXT("USBshutdown\n"));
	DestroyDevices();

	free(qemu_ohci);

	ram = 0;

//#ifdef _DEBUG
	if (conf.Log && usbLog) fclose(usbLog);
//#endif
}

EXPORT_C_(s32) USBopen(void *pDsp) {
	USB_LOG("USBopen\n");
	OSDebugOut(TEXT("USBopen\n"));

#if _WIN32

	HWND hWnd=(HWND)pDsp;

	if (!IsWindow (hWnd) && !IsBadReadPtr ((u32*)hWnd, 4))
		hWnd = *(HWND*)hWnd;
	if (!IsWindow (hWnd))
		hWnd = NULL;
	else
	{
		while (GetWindowLong (hWnd, GWL_STYLE) & WS_CHILD)
			hWnd = GetParent (hWnd);
	}
	gsWnd = hWnd;
	if(msgWindow == NULL)
		InitWindow(hWnd);
#endif

	if (configChanged || (!usb_device1 && !usb_device2))
	{
		configChanged = false;
		CreateDevices(); //TODO Pass pDsp to init?
	}

	//TODO Pass pDsp to open probably so dinput can bind to this HWND
	if(usb_device1 && usb_device1->open) usb_device1->open(usb_device1);
	if(usb_device2 && usb_device2->open) usb_device2->open(usb_device2);
	return 0;
}

EXPORT_C_(void) USBclose() {
	OSDebugOut(TEXT("USBclose\n"));
	if(usb_device1 && usb_device1->close) usb_device1->close(usb_device1);
	if(usb_device2 && usb_device2->close) usb_device2->close(usb_device2);
#if _WIN32
	UninitWindow();
#endif
}

EXPORT_C_(u8) USBread8(u32 addr) {
	USB_LOG("* Invalid 8bit read at address %lx\n", addr);
	return 0;
}

EXPORT_C_(u16) USBread16(u32 addr) {
	USB_LOG("* Invalid 16bit read at address %lx\n", addr);
	return 0;
}

EXPORT_C_(u32) USBread32(u32 addr) {
	u32 hard;

	hard=ohci_mem_read(qemu_ohci,addr);

	USB_LOG("* Known 32bit read at address %lx: %lx\n", addr, hard);

	return hard;
}

EXPORT_C_(void) USBwrite8(u32 addr,  u8 value) {
	USB_LOG("* Invalid 8bit write at address %lx value %x\n", addr, value);
}

EXPORT_C_(void) USBwrite16(u32 addr, u16 value) {
	USB_LOG("* Invalid 16bit write at address %lx value %x\n", addr, value);
}

EXPORT_C_(void) USBwrite32(u32 addr, u32 value) {
	USB_LOG("* Known 32bit write at address %lx value %lx\n", addr, value);
	ohci_mem_write(qemu_ohci,addr,value);
}

EXPORT_C_(void) USBirqCallback(USBcallback callback) {
	_USBirq = callback;
}

extern u32 bits;

EXPORT_C_(int) _USBirqHandler(void)
{
	//fprintf(stderr," * USB: IRQ Acknowledged.\n");
	//qemu_ohci->intr_status&=~bits;
	//TODO Instead of USBasync(), do like nuuvem plugin and
	// do ohci_frame_boundary() here?
#if 0
	// load state - restart interrupts
	if( freeze_load == 1 ) {
		USB_LOG( "_USBirqHandler: Freeze - restart IRQ\n" );

		USBirq(PSXCLK / 1000);

		freeze_load = 0;
		return 0;
	}

	// generate SF packet every 1ms ticks
	if( (qemu_ohci->ctl & OHCI_CTL_HCFS) == OHCI_USB_OPERATIONAL ) {
		USBirq(PSXCLK / 1000);
		qemu_ohci->intr_status |= OHCI_INTR_SF;
	}
	ohci_frame_boundary(qemu_ohci);
#endif

	return 1;
}

EXPORT_C_(USBhandler) USBirqHandler(void) {
	return (USBhandler)_USBirqHandler;
}

EXPORT_C_(void) USBsetRAM(void *mem) {
	ram = (u8*)mem;
	Reset();
}

//TODO update VID/PID and various buffer lengths with changes in usb-pad.c?
EXPORT_C_(s32) USBfreeze(int mode, freezeData *data) {
	USBfreezeData usbd;

	if (mode == FREEZE_LOAD)
	{
		if(data->size < sizeof(USBfreezeData))
		{
			SysMessage(TEXT("ERROR: Unable to load freeze data! Got %d bytes, expected >= %d."), data->size, sizeof(USBfreezeData));
			return -1;
		}

		usbd = *(USBfreezeData*)data->data;
		usbd.freezeID[10] = 0;

		if( strcmp(usbd.freezeID, USBfreezeID) != 0)
		{
			SysMessage(TEXT("ERROR: Unable to load freeze data! Found ID '%s', expected ID '%s'."), usbd.freezeID, USBfreezeID);
			return -1;
		}

		if (data->size != sizeof(USBfreezeData))
			return -1;

		clocks = usbd.cycles;
		remaining = usbd.remaining;
		for(int i=0; i< qemu_ohci->num_ports; i++)
		{
			usbd.t.rhport[i].port.opaque = qemu_ohci;
			//usbd.t.rhport[i].port.ops = qemu_ohci->rhport[i].port.ops;
			usbd.t.rhport[i].port.dev = qemu_ohci->rhport[i].port.dev; // pointers
		}
		*qemu_ohci = usbd.t;

		// WARNING: TODO: Load the state of the attached devices!

	}
	else if (mode == FREEZE_SAVE)
	{
		data->size = sizeof(USBfreezeData);
		data->data = (s8*)malloc(data->size);
		if (data->data == NULL)
			return -1;

		strncpy(usbd.freezeID,  USBfreezeID, strlen(USBfreezeID));
		usbd.t = *qemu_ohci;
		for(int i=0; i< qemu_ohci->num_ports; i++)
		{
			//usbd.t.rhport[i].port.ops = NULL; // pointers
			usbd.t.rhport[i].port.opaque = NULL; // pointers
			usbd.t.rhport[i].port.dev = NULL; // pointers
		}

		usbd.cycles = clocks;
		usbd.remaining = remaining;
		memcpy(data->data, &usbd, data->size);
		//*(USBfreezeData*)data->data = usbd;

		// WARNING: TODO: Save the state of the attached devices!
	}

	return 0;
}

EXPORT_C_(void) USBasync(u32 cycles)
{
	remaining += cycles;
	clocks += remaining;
	if(qemu_ohci->eof_timer>0)
	{
		while(remaining>=qemu_ohci->eof_timer)
		{
			remaining-=qemu_ohci->eof_timer;
			qemu_ohci->eof_timer=0;
			ohci_frame_boundary(qemu_ohci);
		}
		if((remaining>0)&&(qemu_ohci->eof_timer>0))
		{
			s64 m = qemu_ohci->eof_timer;
			if(remaining < m)
				m = remaining;
			qemu_ohci->eof_timer -= m;
			remaining -= m;
		}
	}
	//if(qemu_ohci->eof_timer <= 0)
	//{
	//ohci_frame_boundary(qemu_ohci);
	//}
}

EXPORT_C_(s32) USBtest() {
	return 0;
}

void cpu_physical_memory_rw(u32 addr, u8 *buf, int len, int is_write)
{
	//OSDebugOut(TEXT("%s addr %08X, len %d\n"), is_write ? TEXT("write") : TEXT("read "), addr, len);
	if(is_write)
		memcpy(&(ram[addr]),buf,len);
	else
		memcpy(buf,&(ram[addr]),len);
}

s64 get_clock()
{
	return clocks;
}
<commit_msg>CRLF line endings<commit_after>/*  USBlinuz
 *  Copyright (C) 2002-2004  USBlinuz Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdlib.h>
#include <string>
#include <errno.h>

#include "qemu-usb/vl.h"
#include "USB.h"
#include "deviceproxy.h"

const unsigned char version  = PS2E_USB_VERSION;
const unsigned char revision = 0;
const unsigned char build    = 5;    // increase that with each version
const unsigned char fix      = 2;

static char libraryName[256];

OHCIState *qemu_ohci = NULL;
USBDevice *usb_device1 = NULL;
USBDevice *usb_device2 = NULL;
bool configChanged = false;

Config conf;
char USBfreezeID[] = "USBqemuW01";
typedef struct {
	char freezeID[11];
	s64 cycles;
	s64 remaining;
	OHCIState t;
	int extraData; // for future expansion with the device state
} USBfreezeData;

u8 *ram = 0;
USBcallback _USBirq;
FILE *usbLog;
int64_t usb_frame_time;
int64_t usb_bit_time;

s64 clocks = 0;
s64 remaining = 0;

#if _WIN32
HWND gsWnd=NULL;
#endif

void USBirq(int cycles)
{
	USB_LOG("USBirq.\n");

	_USBirq(cycles);
}

void __Log(const char *fmt, ...) {
	va_list list;

	if (!conf.Log ||!usbLog) return;

	va_start(list, fmt);
	vfprintf(usbLog, fmt, list);
	va_end(list);
}

//Simpler to reset and reattach after USBclose/USBopen
void Reset()
{
	if(qemu_ohci)
		ohci_reset(qemu_ohci);
}

void DestroyDevices()
{
	//FIXME something throws an null ptr exception?
	if(qemu_ohci && qemu_ohci->rhport[PLAYER_ONE_PORT].port.dev) {
		qemu_ohci->rhport[PLAYER_ONE_PORT].port.dev->handle_destroy(qemu_ohci->rhport[PLAYER_ONE_PORT].port.dev);
		qemu_ohci->rhport[PLAYER_ONE_PORT].port.dev = NULL;
	}
	else if(usb_device1) //maybe redundant
		usb_device1->handle_destroy(usb_device1);

	if(qemu_ohci && qemu_ohci->rhport[PLAYER_TWO_PORT].port.dev) {
		qemu_ohci->rhport[PLAYER_TWO_PORT].port.dev->handle_destroy(qemu_ohci->rhport[PLAYER_TWO_PORT].port.dev);
		qemu_ohci->rhport[PLAYER_TWO_PORT].port.dev = NULL;
	}
	else if(usb_device2)
		usb_device2->handle_destroy(usb_device2);

	usb_device1 = NULL;
	usb_device2 = NULL;
}

void CreateDevices()
{
	DeviceProxyBase *devProxy;
	if(!qemu_ohci) return; //No USBinit yet ie. called from config. dialog
	DestroyDevices();

	if (!conf.Port1.empty())
	{
		devProxy = RegisterDevice::instance().Device(conf.Port1);
		if (devProxy)
			usb_device1 = devProxy->CreateDevice(PLAYER_ONE_PORT);
		else
			SysMessage(TEXT("Device 1: Unknown device type"));
	}

	if (!conf.Port0.empty())
	{
		devProxy = RegisterDevice::instance().Device(conf.Port0);
		if (devProxy)
			usb_device2 = devProxy->CreateDevice(PLAYER_TWO_PORT);
		else
			SysMessage(TEXT("Device 2: Unknown device type"));
	}

	//No need for NULL check. NULL device means detach port.
	qemu_ohci->rhport[PLAYER_ONE_PORT].port.attach(&(qemu_ohci->rhport[PLAYER_ONE_PORT].port), usb_device1);
	qemu_ohci->rhport[PLAYER_TWO_PORT].port.attach(&(qemu_ohci->rhport[PLAYER_TWO_PORT].port), usb_device2);
}

EXPORT_C_(u32) PS2EgetLibType() {
	return PS2E_LT_USB;
}

EXPORT_C_(char*) PS2EgetLibName() {

#ifdef _MSC_VER
	sprintf_s
#else
	sprintf
#endif
	( libraryName, "Qemu USB Driver (Wheel) (" __DATE__ ")"
#ifdef _DEBUG
		" (debug)"
#endif
	);

	return libraryName;
}

EXPORT_C_(u32) PS2EgetLibVersion2(u32 type) {
	return (version<<16) | (revision<<8) | build | (fix << 24);
}

EXPORT_C_(s32) USBinit() {
	OSDebugOut(TEXT("USBinit\n"));
	LoadConfig();

	if (conf.Log)
	{
		usbLog = fopen("logs/usbLog.txt", "w");
		if(usbLog) setvbuf(usbLog, NULL,  _IONBF, 0);
		USB_LOG("usbqemu wheel mod plugin version %d,%d\n",revision,build);
		USB_LOG("USBinit\n");
	}

	qemu_ohci = ohci_create(0x1f801600,2);
	if(!qemu_ohci) return 1;

	return 0;
}

EXPORT_C_(void) USBshutdown() {

	OSDebugOut(TEXT("USBshutdown\n"));
	DestroyDevices();

	free(qemu_ohci);

	ram = 0;

//#ifdef _DEBUG
	if (conf.Log && usbLog) fclose(usbLog);
//#endif
}

EXPORT_C_(s32) USBopen(void *pDsp) {
	USB_LOG("USBopen\n");
	OSDebugOut(TEXT("USBopen\n"));

#if _WIN32

	HWND hWnd=(HWND)pDsp;

	if (!IsWindow (hWnd) && !IsBadReadPtr ((u32*)hWnd, 4))
		hWnd = *(HWND*)hWnd;
	if (!IsWindow (hWnd))
		hWnd = NULL;
	else
	{
		while (GetWindowLong (hWnd, GWL_STYLE) & WS_CHILD)
			hWnd = GetParent (hWnd);
	}
	gsWnd = hWnd;
	if(msgWindow == NULL)
		InitWindow(hWnd);
#endif

	if (configChanged || (!usb_device1 && !usb_device2))
	{
		configChanged = false;
		CreateDevices(); //TODO Pass pDsp to init?
	}

	//TODO Pass pDsp to open probably so dinput can bind to this HWND
	if(usb_device1 && usb_device1->open) usb_device1->open(usb_device1);
	if(usb_device2 && usb_device2->open) usb_device2->open(usb_device2);
	return 0;
}

EXPORT_C_(void) USBclose() {
	OSDebugOut(TEXT("USBclose\n"));
	if(usb_device1 && usb_device1->close) usb_device1->close(usb_device1);
	if(usb_device2 && usb_device2->close) usb_device2->close(usb_device2);
#if _WIN32
	UninitWindow();
#endif
}

EXPORT_C_(u8) USBread8(u32 addr) {
	USB_LOG("* Invalid 8bit read at address %lx\n", addr);
	return 0;
}

EXPORT_C_(u16) USBread16(u32 addr) {
	USB_LOG("* Invalid 16bit read at address %lx\n", addr);
	return 0;
}

EXPORT_C_(u32) USBread32(u32 addr) {
	u32 hard;

	hard=ohci_mem_read(qemu_ohci,addr);

	USB_LOG("* Known 32bit read at address %lx: %lx\n", addr, hard);

	return hard;
}

EXPORT_C_(void) USBwrite8(u32 addr,  u8 value) {
	USB_LOG("* Invalid 8bit write at address %lx value %x\n", addr, value);
}

EXPORT_C_(void) USBwrite16(u32 addr, u16 value) {
	USB_LOG("* Invalid 16bit write at address %lx value %x\n", addr, value);
}

EXPORT_C_(void) USBwrite32(u32 addr, u32 value) {
	USB_LOG("* Known 32bit write at address %lx value %lx\n", addr, value);
	ohci_mem_write(qemu_ohci,addr,value);
}

EXPORT_C_(void) USBirqCallback(USBcallback callback) {
	_USBirq = callback;
}

extern u32 bits;

EXPORT_C_(int) _USBirqHandler(void)
{
	//fprintf(stderr," * USB: IRQ Acknowledged.\n");
	//qemu_ohci->intr_status&=~bits;
	//TODO Instead of USBasync(), do like nuuvem plugin and
	// do ohci_frame_boundary() here?
#if 0
	// load state - restart interrupts
	if( freeze_load == 1 ) {
		USB_LOG( "_USBirqHandler: Freeze - restart IRQ\n" );

		USBirq(PSXCLK / 1000);

		freeze_load = 0;
		return 0;
	}

	// generate SF packet every 1ms ticks
	if( (qemu_ohci->ctl & OHCI_CTL_HCFS) == OHCI_USB_OPERATIONAL ) {
		USBirq(PSXCLK / 1000);
		qemu_ohci->intr_status |= OHCI_INTR_SF;
	}
	ohci_frame_boundary(qemu_ohci);
#endif

	return 1;
}

EXPORT_C_(USBhandler) USBirqHandler(void) {
	return (USBhandler)_USBirqHandler;
}

EXPORT_C_(void) USBsetRAM(void *mem) {
	ram = (u8*)mem;
	Reset();
}

//TODO update VID/PID and various buffer lengths with changes in usb-pad.c?
EXPORT_C_(s32) USBfreeze(int mode, freezeData *data) {
	USBfreezeData usbd;

	if (mode == FREEZE_LOAD)
	{
		if(data->size < sizeof(USBfreezeData))
		{
			SysMessage(TEXT("ERROR: Unable to load freeze data! Got %d bytes, expected >= %d."), data->size, sizeof(USBfreezeData));
			return -1;
		}

		usbd = *(USBfreezeData*)data->data;
		usbd.freezeID[10] = 0;

		if( strcmp(usbd.freezeID, USBfreezeID) != 0)
		{
			SysMessage(TEXT("ERROR: Unable to load freeze data! Found ID '%s', expected ID '%s'."), usbd.freezeID, USBfreezeID);
			return -1;
		}

		if (data->size != sizeof(USBfreezeData))
			return -1;

		clocks = usbd.cycles;
		remaining = usbd.remaining;
		for(int i=0; i< qemu_ohci->num_ports; i++)
		{
			usbd.t.rhport[i].port.opaque = qemu_ohci;
			//usbd.t.rhport[i].port.ops = qemu_ohci->rhport[i].port.ops;
			usbd.t.rhport[i].port.dev = qemu_ohci->rhport[i].port.dev; // pointers
		}
		*qemu_ohci = usbd.t;

		// WARNING: TODO: Load the state of the attached devices!

	}
	else if (mode == FREEZE_SAVE)
	{
		data->size = sizeof(USBfreezeData);
		data->data = (s8*)malloc(data->size);
		if (data->data == NULL)
			return -1;

		strncpy(usbd.freezeID,  USBfreezeID, strlen(USBfreezeID));
		usbd.t = *qemu_ohci;
		for(int i=0; i< qemu_ohci->num_ports; i++)
		{
			//usbd.t.rhport[i].port.ops = NULL; // pointers
			usbd.t.rhport[i].port.opaque = NULL; // pointers
			usbd.t.rhport[i].port.dev = NULL; // pointers
		}

		usbd.cycles = clocks;
		usbd.remaining = remaining;
		memcpy(data->data, &usbd, data->size);
		//*(USBfreezeData*)data->data = usbd;

		// WARNING: TODO: Save the state of the attached devices!
	}

	return 0;
}

EXPORT_C_(void) USBasync(u32 cycles)
{
	remaining += cycles;
	clocks += remaining;
	if(qemu_ohci->eof_timer>0)
	{
		while(remaining>=qemu_ohci->eof_timer)
		{
			remaining-=qemu_ohci->eof_timer;
			qemu_ohci->eof_timer=0;
			ohci_frame_boundary(qemu_ohci);
		}
		if((remaining>0)&&(qemu_ohci->eof_timer>0))
		{
			s64 m = qemu_ohci->eof_timer;
			if(remaining < m)
				m = remaining;
			qemu_ohci->eof_timer -= m;
			remaining -= m;
		}
	}
	//if(qemu_ohci->eof_timer <= 0)
	//{
	//ohci_frame_boundary(qemu_ohci);
	//}
}

EXPORT_C_(s32) USBtest() {
	return 0;
}

void cpu_physical_memory_rw(u32 addr, u8 *buf, int len, int is_write)
{
	//OSDebugOut(TEXT("%s addr %08X, len %d\n"), is_write ? TEXT("write") : TEXT("read "), addr, len);
	if(is_write)
		memcpy(&(ram[addr]),buf,len);
	else
		memcpy(buf,&(ram[addr]),len);
}

s64 get_clock()
{
	return clocks;
}
<|endoftext|>