/** Execute the given command in the target VM. */
  InputStream execute(String cmd, Object... args) throws AgentLoadException, IOException {
    assert args.length <= 3; // includes null

    // did we detach?
    String p;
    synchronized (this) {
      if (this.path == null) {
        throw new IOException("Detached from target VM");
      }
      p = this.path;
    }

    // create UNIX socket
    int s = socket();

    // connect to target VM
    try {
      connect(s, p);
    } catch (IOException x) {
      close(s);
      throw x;
    }

    IOException ioe = null;

    // connected - write request
    // <ver> <cmd> <args...>
    try {
      writeString(s, PROTOCOL_VERSION);
      writeString(s, cmd);

      for (int i = 0; i < 3; i++) {
        if (i < args.length && args[i] != null) {
          writeString(s, (String) args[i]);
        } else {
          writeString(s, "");
        }
      }
    } catch (IOException x) {
      ioe = x;
    }

    // Create an input stream to read reply
    SocketInputStream sis = new SocketInputStream(s);

    // Read the command completion status
    int completionStatus;
    try {
      completionStatus = readInt(sis);
    } catch (IOException x) {
      sis.close();
      if (ioe != null) {
        throw ioe;
      } else {
        throw x;
      }
    }

    if (completionStatus != 0) {
      sis.close();

      // In the event of a protocol mismatch then the target VM
      // returns a known error so that we can throw a reasonable
      // error.
      if (completionStatus == ATTACH_ERROR_BADVERSION) {
        throw new IOException("Protocol mismatch with target VM");
      }

      // Special-case the "load" command so that the right exception is
      // thrown.
      if (cmd.equals("load")) {
        throw new AgentLoadException("Failed to load agent library");
      } else {
        throw new IOException("Command failed in target VM");
      }
    }

    // Return the input stream so that the command output can be read
    return sis;
  }