def Map(port_pairs, device, tool=None):
    """Runs the forwarder.

    Args:
      port_pairs: A list of tuples (device_port, host_port) to forward. Note
                 that you can specify 0 as a device_port, in which case a
                 port will by dynamically assigned on the device. You can
                 get the number of the assigned port using the
                 DevicePortForHostPort method.
      device: A DeviceUtils instance.
      tool: Tool class to use to get wrapper, if necessary, for executing the
            forwarder (see valgrind_tools.py).

    Raises:
      Exception on failure to forward the port.
    """
    if not tool:
      tool = base_tool.BaseTool()
    with _FileLock(Forwarder._LOCK_PATH):
      instance = Forwarder._GetInstanceLocked(tool)
      instance._InitDeviceLocked(device, tool)

      device_serial = str(device)
      map_arg_lists = [[
          '--adb=' + adb_wrapper.AdbWrapper.GetAdbPath(),
          '--serial-id=' + device_serial, '--map',
          str(device_port),
          str(host_port)
      ] for device_port, host_port in port_pairs]
      logger.info('Forwarding using commands: %s', map_arg_lists)

      for map_arg_list in map_arg_lists:
        try:
          map_cmd = [instance._host_forwarder_path] + map_arg_list
          (exit_code, output) = cmd_helper.GetCmdStatusAndOutputWithTimeout(
              map_cmd, Forwarder._TIMEOUT)
        except cmd_helper.TimeoutError as e:
          raise HostForwarderError(
              '`%s` timed out:\n%s' % (' '.join(map_cmd), e.output))
        except OSError as e:
          if e.errno == 2:
            raise HostForwarderError('Unable to start host forwarder. '
                                     'Make sure you have built host_forwarder.')
          else:
            raise
        if exit_code != 0:
          try:
            instance._KillDeviceLocked(device, tool)
          except (device_errors.CommandFailedError,
                  device_errors.DeviceUnreachableError):
            # We don't want the failure to kill the device forwarder to
            # supersede the original failure to map.
            logger.warning(
                'Failed to kill the device forwarder after map failure: %s',
                str(e))
          _LogMapFailureDiagnostics(device)
          formatted_output = ('\n'.join(output)
                              if isinstance(output, list) else output)
          raise HostForwarderError(
              '`%s` exited with %d:\n%s' % (' '.join(map_cmd), exit_code,
                                            formatted_output))
        tokens = output.split(':')
        if len(tokens) != 2:
          raise HostForwarderError('Unexpected host forwarder output "%s", '
                                   'expected "device_port:host_port"' % output)
        device_port = int(tokens[0])
        host_port = int(tokens[1])
        serial_with_port = (device_serial, device_port)
        instance._device_to_host_port_map[serial_with_port] = host_port
        instance._host_to_device_port_map[host_port] = serial_with_port
        logger.info('Forwarding device port: %d to host port: %d.', device_port,
                    host_port)