// userspace conversions tapset
// Copyright (C) 2005-2018 Red Hat Inc.
// Copyright (C) 2007 Intel Corporation.
//
// This file is part of systemtap, and is free software.  You can
// redistribute it and/or modify it under the terms of the GNU General
// Public License (GPL); either version 2, or (at your option) any
// later version.

// implement in terms of runtime/loc2c-runtime.h macro uread()
%{
#define STP_GET_USER(t)							\
	do {								\
		t *_ptr = (t*) (uintptr_t) STAP_ARG_addr;		\
		STAP_RETVALUE = uread (_ptr);				\
	} while (0)
%}

/**
 * sfunction user_string - Retrieves string from user space
 *
 * @addr: the user space address to retrieve the string from
 *
 * Description: Returns the null terminated C string from a given user space
 * memory address. Reports an error on the rare cases when userspace
 * data is not accessible.
 */
function user_string:string (addr:long) {
	return user_string_n(addr, @MAXSTRINGLEN)
}

/**
 * sfunction user_string - Retrieves string from user space with alternative error string
 *
 * @addr: the user space address to retrieve the string from
 * @err_msg: the error message to return when data isn't available
 *
 * Description: Returns the null terminated C string from a given user space
 * memory address. Reports the given error message on the rare cases when
 * userspace data is not accessible.
 */
function user_string:string (addr:long, err_msg:string) {
	return user_string_n(addr, @MAXSTRINGLEN, err_msg)
}
function user_string2:string (addr:long, err_msg:string) {
	return user_string_n(addr, @MAXSTRINGLEN, err_msg)
}

/**
 * sfunction user_string_warn - Retrieves string from user space
 *
 * @addr: the user space address to retrieve the string from
 *
 * Description: Returns the null terminated C string from a given user space
 * memory address. Reports "" on the rare cases when userspace data is
 * not accessible and warns (but does not abort) about the failure.
 */
function user_string_warn:string (addr:long) {
%(systemtap_v < "2.3" %? // PR15044
	return user_string_warn(addr, "<unknown>")
%:
	return user_string_warn(addr, "")
%)
}


/**
 * sfunction user_string_warn - Retrieves string from user space with alternative warning string
 *
 * @addr: the user space address to retrieve the string from
 * @warn_msg: the warning message to return when data isn't available
 * 
 * Description: Returns the null terminated C string from a given user space
 * memory address. Reports the given warning message on the rare cases
 * when userspace data is not accessible and warns (but does not abort)
 * about the failure.
 */
function user_string_warn:string (addr:long, warn_msg:string) {
	return user_string_n_warn(addr, @MAXSTRINGLEN, warn_msg)
}
function user_string2_warn:string (addr:long, warn_msg:string) {
	return user_string_n_warn(addr, @MAXSTRINGLEN, warn_msg)
}

/**
 * sfunction user_string_quoted - Retrieves and quotes string from user space
 *
 * @addr: the user space address to retrieve the string from
 *
 * Description: Returns the null terminated C string from a given user space
 * memory address where any ASCII characters that are not printable are
 * replaced by the corresponding escape sequence in the returned string. Note
 * that the string will be surrounded by double quotes. On the rare cases when
 * userspace data is not accessible at the given address, the address itself is
 * returned as a string, without double quotes.
 */
function user_string_quoted:string (addr:long) {
  try { return string_quoted(user_string(addr)) } catch { return sprintf("0x%x", addr) }
}




/**
 * sfunction user_string_n - Retrieves string of given length from user space
 *
 * @addr: the user space address to retrieve the string from
 * @n: the maximum length of the string (if not null terminated)
 *
 * Description: Returns the C string of a maximum given length from a
 * given user space address. Reports an error on the rare cases
 * when userspace data is not accessible at the given address.
 */
function user_string_n:string (addr:long, n:long)
%( systemtap_v < "2.3" %? // PR15044
	{ return user_string_n(addr, n, "<unknown>") }
%:
	%{ /* pure */ /* myproc-unprivileged */
		long rc;
		int64_t len = clamp_t(int64_t, STAP_ARG_n + 1, 1,
				      MAXSTRINGLEN - 1);
		rc = _stp_strncpy_from_user(STAP_RETVALUE,
			(char __user *) (uintptr_t) STAP_ARG_addr, len);
		if (rc < 0) {
			strlcpy(STAP_RETVALUE, "<unknown>", MAXSTRINGLEN);
			snprintf (CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
				"user string copy fault %ld at %p [man error::fault]", rc,
				(void *) (uintptr_t) STAP_ARG_addr);
			CONTEXT->last_error = CONTEXT->error_buffer;
		} else
			STAP_RETVALUE[len - 1] = '\0';
	%}
%)

/**
 * sfunction user_string_n - Retrieves string of given length from user space
 *
 * @addr: the user space address to retrieve the string from
 * @n: the maximum length of the string (if not null terminated)
 * @err_msg: the error message to return when data isn't available
 *
 * Description: Returns the C string of a maximum given length from a
 * given user space address. Returns the given error message string on
 * the rare cases when userspace data is not accessible at the given
 * address.
 */
function user_string_n:string (addr:long, n:long, err_msg:string)
%{ /* pure */ /* myproc-unprivileged */ /* unmodified-fnargs */
  int64_t len = clamp_t(int64_t, STAP_ARG_n + 1, 1, MAXSTRINGLEN - 1);
  if (_stp_strncpy_from_user(STAP_RETVALUE, 
        (char __user *) (uintptr_t) STAP_ARG_addr, len) < 0)
    strlcpy(STAP_RETVALUE, STAP_ARG_err_msg, MAXSTRINGLEN);
  else
    STAP_RETVALUE[len - 1] = '\0';
%}
function user_string_n2:string (addr:long, n:long, err_msg:string) {
  return user_string_n(addr, n, err_msg);
}

/**
 * sfunction user_string_n_warn - Retrieves string from user space
 *
 * @addr: the user space address to retrieve the string from
 * @n: the maximum length of the string (if not null terminated)
 *
 * Description: Returns up to n characters of a C string from a given
 * user space memory address. Reports "<unknown>" on the rare cases
 * when userspace data is not accessible and warns (but does not abort)
 * about the failure.
 */
function user_string_n_warn:string (addr:long, n:long) {
%(systemtap_v < "2.3" %? // PR15044
	return user_string_n_warn(addr, n, "<unknown>")
%:
	return user_string_n_warn(addr, n, "")
%)
}

/**
 * sfunction user_string_n_warn - Retrieves string from user space with alternative warning string
 *
 * @addr: the user space address to retrieve the string from
 * @n: the maximum length of the string (if not null terminated)
 * @warn_msg: the warning message to return when data isn't available
 * 
 * Description: Returns up to n characters of a C string from a given
 * user space memory address. Reports the given warning message on the
 * rare cases when userspace data is not accessible and warns (but does
 * not abort) about the failure.
 */
function user_string_n_warn:string (addr:long, n:long, warn_msg:string)
%{ /* pure */ /* myproc-unprivileged */ /* unmodified-fnargs */
	int64_t len = clamp_t(int64_t, STAP_ARG_n + 1, 1, MAXSTRINGLEN - 1);
	long rc;
	rc = _stp_strncpy_from_user(STAP_RETVALUE,
			(char __user *) (uintptr_t) STAP_ARG_addr, len);
	if (rc < 0) {
		// NB: using error_buffer to get local space for the warning, but we're
		// not aborting, so leave last_error alone.
		snprintf (CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
				"user string copy fault %ld at %p [man error::fault]", rc,
				(void *) (uintptr_t) STAP_ARG_addr);
		_stp_warn(CONTEXT->error_buffer);
		strlcpy (STAP_RETVALUE, STAP_ARG_warn_msg, MAXSTRINGLEN);
	} else
		STAP_RETVALUE[len - 1] = '\0';
%}
function user_string2_n_warn:string (addr:long, n:long, warn_msg:string) {
  user_string_n_warn(addr, n, warn_msg);
}

/**
 * sfunction user_string_n_quoted - Retrieves and quotes string from user space
 *
 * @addr: the user space address to retrieve the string from
 * @n: the maximum length of the string (if not null terminated)
 *
 * Description: Returns up to n characters of a C string from the given
 * user space memory address where any ASCII characters that are not
 * printable are replaced by the corresponding escape sequence in the
 * returned string. Note that the string will be surrounded by double quotes.
 * On the rare cases when userspace data is not accessible at the given address,
 * the address itself is returned as a string, without double quotes.
 */
function user_string_n_quoted:string (addr:long, n:long) {
%(systemtap_v < "2.4" %?
	// We used to count n by output characters
	return user_string_n_quoted(addr, @MAXSTRINGLEN, n)
%:
	return user_string_n_quoted(addr, n, @MAXSTRINGLEN)
%)
}

/**
 * sfunction user_string_n_quoted - Retrieves and quotes string from user space
 *
 * @addr: the user space address to retrieve the string from
 * @inlen: the maximum length of the string to read (if not null terminated)
 * @outlen: the maximum length of the output string
 *
 * Description: Reads up to inlen characters of a C string from the given user
 * space memory address, and returns up to outlen characters, where any ASCII
 * characters that are not printable are replaced by the corresponding escape
 * sequence in the returned string. Note that the string will be surrounded by
 * double quotes.  On the rare cases when userspace data is not accessible at
 * the given address, the address itself is returned as a string, without
 * double quotes.
 */
function user_string_n_quoted:string (addr:long, inlen:long, outlen:long)
%( systemtap_v >= "3.0" %?
   {
     try
       {
         __str = string_quoted(user_string_n(addr, inlen))
         if (strlen(__str) > outlen-2) // room for the double-quotes plus \ldots
           __str = (substr(__str,0,outlen-4) . "\"...")
         return __str
       }
     catch
       {
         return sprintf("0x%x", addr)
       }
   }
%:   

%{ /* pure */ /* myproc-unprivileged */
	// Note the lack of STAP_ARG_n+1 as in other funcs() -- PR15617
	int64_t inlen = clamp_t(int64_t, STAP_ARG_inlen, 0, MAXSTRINGLEN);
	int64_t outlen = clamp_t(int64_t, STAP_ARG_outlen, 0, MAXSTRINGLEN);
	if (STAP_ARG_addr == 0)
#if STAP_COMPAT_VERSION < STAP_VERSION(2,3) // PR15044
		strlcpy(STAP_RETVALUE, "NULL", MAXSTRINGLEN);
#else
		snprintf(STAP_RETVALUE, MAXSTRINGLEN, "%p", (void *)(uintptr_t)STAP_ARG_addr);
#endif
	else {
		int rc = _stp_text_str(STAP_RETVALUE,
			(char *)(uintptr_t)STAP_ARG_addr, inlen, outlen, 1, 1, 0);
		if (rc < 0) {
#if STAP_COMPAT_VERSION < STAP_VERSION(2,3) // PR15044
			strlcpy(STAP_RETVALUE, "<unknown>", MAXSTRINGLEN);
#else
#ifdef CONFIG_COMPAT
			if (_stp_is_compat_task())
				snprintf(STAP_RETVALUE, MAXSTRINGLEN, "%x",
					 (compat_long_t)STAP_ARG_addr);
			else
#endif
				snprintf(STAP_RETVALUE, MAXSTRINGLEN, "%p",
					 (void *)(uintptr_t)STAP_ARG_addr);
#endif
		}
	}
%}
   
%)
function user_string_n2_quoted:string (addr:long, inlen:long, outlen:long) {
  return user_string_n_quoted(addr, inlen, outlen);

}

/**
 * sfunction user_string_utf32 - Retrieves UTF-32 string from user memory
 * @addr: The user address to retrieve the string from
 *
 * Description: This function returns a null terminated UTF-8 string converted
 * from the UTF-32 string at a given user memory address. Reports an error on
 * string copy fault or conversion error.
 */
function user_string_utf32:string (addr:long) %{ /* pure */ /* myproc-unprivileged */
  __label__ deref_fault;
  int rc = 0, len = MAXSTRINGLEN;
  uint32_t c32, *source = (uint32_t*)(uintptr_t)STAP_ARG_addr;
  char *destination = STAP_RETVALUE;

  *destination = '\0';
  while (len > 1 && (c32 = uread(source))) {
    if ((rc = _stp_convert_utf32(destination, len, c32)) <= 0) {
      if (rc < 0) {
        snprintf (CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
                  "invalid UTF-32 character U+%X at 0x%p", c32, source);
        CONTEXT->last_error = CONTEXT->error_buffer;
      }
      break;
    }
    ++source;
    destination += rc;
    len -= rc;
  }

  if (0) {
deref_fault: /* branched to from deref_string() */
    snprintf (CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
              "user string copy fault at 0x%p [man error::fault]", source);
    CONTEXT->last_error = CONTEXT->error_buffer;
  }
%}

/**
 * sfunction user_string_utf32 - Retrieves UTF-32 string from user memory with alternative error string
 * @addr: The user address to retrieve the string from
 * @err_msg: The error message to return when data isn't available
 *
 * Description: This function returns a null terminated UTF-8 string converted
 * from the UTF-32 string at a given user memory address. Reports the given
 * error message on string copy fault or conversion error.
 */
function user_string_utf32:string (addr:long, err_msg:string) {
  try { return user_string_utf32(addr) } catch { return err_msg }
}
function user_string2_utf32:string (addr:long, err_msg:string) {
  return user_string_utf32 (addr, err_msg);
}


/**
 * sfunction user_string_quoted_utf32 - Quote given user UTF-32 string.
 * @addr: The user address to retrieve the string from
 *
 * Description: This function combines quoting as per @string_quoted
 * and UTF-32 decoding as per @user_string_utf32.
 */
function user_string_quoted_utf32:string (addr:long) {
  try { return string_quoted(user_string_utf32(addr)) } catch { return sprintf("0x%x", addr) }
}



/**
 * sfunction user_string_utf16 - Retrieves UTF-16 string from user memory
 * @addr: The user address to retrieve the string from
 *
 * Description: This function returns a null terminated UTF-8 string converted
 * from the UTF-16 string at a given user memory address. Reports an error on
 * string copy fault or conversion error.
 */
function user_string_utf16:string (addr:long) %{ /* pure */ /* myproc-unprivileged */
  __label__ deref_fault;
  int rc = 0, len = MAXSTRINGLEN;
  uint32_t c32;
  uint16_t c16low, *source = (uint16_t*)(uintptr_t)STAP_ARG_addr;
  char *destination = STAP_RETVALUE;

  *destination = '\0';
  while (len > 1 && (c32 = uread(source))) {
    /* Check for a UTF-16 high surrogate, then its low pair, and combine them.
     * Broken surrogates will just fall through to _stp_convert_utf32 and get
     * flagged as an error there.  (Or even allowed, if we decide to be lax.)
     */
    if (c32 >= 0xD800 && c32 <= 0xDBFF) {
      ++source;
      c16low = uread(source);
      if (c16low >= 0xDC00 && c16low <= 0xDFFF)
        c32 = 0x10000 + ((c32 & 0x3FF) << 10) + (c16low & 0x3FF);
      else
        --source;
    }

    if ((rc = _stp_convert_utf32(destination, len, c32)) <= 0) {
      if (rc < 0) {
        snprintf (CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
                  "invalid UTF-16 character U+%X at 0x%p", c32, source);
        CONTEXT->last_error = CONTEXT->error_buffer;
      }
      break;
    }
    ++source;
    destination += rc;
    len -= rc;
  }

  if (0) {
deref_fault: /* branched to from deref_string() */
    snprintf (CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
              "user string copy fault at 0x%p [man error::fault]", source);
    CONTEXT->last_error = CONTEXT->error_buffer;
  }
%}

/**
 * sfunction user_string_utf16 - Retrieves UTF-16 string from user memory with alternative error string
 * @addr: The user address to retrieve the string from
 * @err_msg: The error message to return when data isn't available
 *
 * Description: This function returns a null terminated UTF-8 string converted
 * from the UTF-16 string at a given user memory address. Reports the given
 * error message on string copy fault or conversion error.
 */
function user_string_utf16:string (addr:long, err_msg:string) {
  try { return user_string_utf16(addr) } catch { return err_msg }
}
function user_string2_utf16:string (addr:long, err_msg:string) {
  return user_string_utf16(addr, err_msg);
}


/**
 * sfunction user_string_quoted_utf16 - Quote given user UTF-16 string.
 * @addr: The user address to retrieve the string from
 *
 * Description: This function combines quoting as per @string_quoted
 * and UTF-16 decoding as per @user_string_utf16.
 */
function user_string_quoted_utf16:string (addr:long) {
  try { return string_quoted(user_string_utf16(addr)) } catch { return sprintf("0x%x", addr) }
}


/**
 * sfunction user_char_error - Retrieves a char value stored in user space
 *
 * @addr: the user space address to retrieve the char from
 *
 * Description: Returns the char value from a given user space address.
 * If the user space data is not accessible, an error will occur.
 */
function user_char_error:long (addr:long) %{ /* pure */ /* myproc-unprivileged */
	STP_GET_USER(char);
	CATCH_DEREF_FAULT();
%}

/**
 * sfunction user_char - Retrieves a char value stored in user space
 *
 * @addr: the user space address to retrieve the char from
 *
 * Description: Returns the char value from a given user space address.
 * Returns zero when user space data is not accessible.
 */
function user_char:long (addr:long) {
	try { return user_char_error(addr) } catch { return 0 }
}

/**
 * sfunction user_char_warn - Retrieves a char value stored in user space
 *
 * @addr: the user space address to retrieve the char from
 *
 * Description: Returns the char value from a given user space
 * address.  Returns zero when user space data is not accessible and
 * warns about the failure (but does not error).
 */
function user_char_warn:long (addr:long) {
	try {
		return user_char_error(addr)
	} catch {
		warn(sprintf("user char copy fault %p [man error::fault]",
			     addr))
		return 0
	}
}

/**
 * sfunction user_short_error - Retrieves a short value stored in user space
 *
 * @addr: the user space address to retrieve the short from
 *
 * Description: Returns the short value from a given user space address.
 * If the user space data is not accessible, an error will occur.
 */
function user_short_error:long (addr:long) %{ /* pure */ /* myproc-unprivileged */
	STP_GET_USER(short);
	CATCH_DEREF_FAULT();
%}

/**
 * sfunction user_short - Retrieves a short value stored in user space
 *
 * @addr: the user space address to retrieve the short from
 *
 * Description: Returns the short value from a given user space address.
 * Returns zero when user space data is not accessible.
 */
function user_short:long (addr:long) {
	try { return user_short_error(addr) } catch { return 0 }
}

/**
 * sfunction user_short_warn - Retrieves a short value stored in user space
 *
 * @addr: the user space address to retrieve the short from
 *
 * Description: Returns the short value from a given user space
 * address.  Returns zero when user space data is not accessible and
 * warns about the failure (but does not error).
 */
function user_short_warn:long (addr:long) {
	try {
		return user_short_error(addr)
	} catch {
		warn(sprintf("user short copy fault %p [man error::fault]",
			     addr))
		return 0
	}
}

/**
 * sfunction user_ushort_error - Retrieves an unsigned short value stored in user space
 *
 * @addr: the user space address to retrieve the unsigned short from
 *
 * Description: Returns the unsigned short value from a given user
 * space address.  If the user space data is not accessible, an error
 * will occur.
 */
function user_ushort_error:long (addr:long) %{ /* pure */ /* myproc-unprivileged */
	STP_GET_USER(unsigned short);
	CATCH_DEREF_FAULT();
%}

/**
 * sfunction user_ushort - Retrieves an unsigned short value stored in user space
 *
 * @addr: the user space address to retrieve the unsigned short from
 *
 * Description: Returns the unsigned short value from a given user
 * space address.  Returns zero when user space data is not accessible.
 */
function user_ushort:long (addr:long)
{
	try { return user_ushort_error(addr) } catch { return 0 }
}

/**
 * sfunction user_ushort_warn - Retrieves an unsigned short value stored in user space
 *
 * @addr: the user space address to retrieve the unsigned short from
 *
 * Description: Returns the unsigned short value from a given user
 * space address.  Returns zero when user space data is not accessible
 * and warns about the failure (but does not error).
 */
function user_ushort_warn:long (addr:long)
{
	try {
		return user_ushort_error(addr)
	} catch {
		warn(sprintf("user unsigned short copy fault %p [man error::fault]",
			     addr))
		return 0
	}
}

/**
 * sfunction user_int_error - Retrieves an int value stored in user space
 *
 * @addr: the user space address to retrieve the int from
 *
 * Description: Returns the int value from a given user space address.
 * If the user space data is not accessible, an error will occur.
 */
function user_int_error:long (addr:long) %{ /* pure */ /* myproc-unprivileged */
	STP_GET_USER(int);
	CATCH_DEREF_FAULT();
%}

/**
 * sfunction user_int - Retrieves an int value stored in user space
 *
 * @addr: the user space address to retrieve the int from
 *
 * Description: Returns the int value from a given user space address.
 * Returns zero when user space data is not accessible.
 */
function user_int:long (addr:long)
{
	try { return user_int_error(addr) } catch { return 0 }
}

/**
 * sfunction user_int_warn - Retrieves an int value stored in user space
 *
 * @addr: the user space address to retrieve the int from
 *
 * Description: Returns the int value from a given user space address.
 * Returns zero when user space data is not accessible and warns about
 * the failure (but does not error).
 */
function user_int_warn:long (addr:long)
{
	try {
		return user_int_error(addr)
	} catch {
		warn(sprintf("user int copy fault %p [man error::fault]",
			     addr))
		return 0
	}
}

/**
 * sfunction user_long_error - Retrieves a long value stored in user space
 *
 * @addr: the user space address to retrieve the long from
 *
 * Description: Returns the long value from a given user space
 * address.  If the user space data is not accessible, an error will
 * occur.  Note that the size of the long depends on the architecture
 * of the current user space task (for those architectures that
 * support both 64/32 bit compat tasks).
 */
function user_long_error:long (addr:long) %{ /* pure */ /* myproc-unprivileged */
#ifdef CONFIG_COMPAT
	if (_stp_is_compat_task())
		STP_GET_USER(compat_long_t);
	else
#endif
		STP_GET_USER(long);
	CATCH_DEREF_FAULT();
%}

/**
 * sfunction user_long - Retrieves a long value stored in user space
 *
 * @addr: the user space address to retrieve the long from
 *
 * Description: Returns the long value from a given user space address.
 * Returns zero when user space data is not accessible. Note that the
 * size of the long depends on the architecture of the current user space
 * task (for those architectures that support both 64/32 bit compat tasks).
 */
function user_long:long (addr:long) {
	try { return user_long_error(addr) } catch { return 0 }
}

/**
 * sfunction user_long_warn - Retrieves a long value stored in user space
 *
 * @addr: the user space address to retrieve the long from
 *
 * Description: Returns the long value from a given user space
 * address.  Returns zero when user space data is not accessible and
 * warns about the failure (but does not error).  Note that the size
 * of the long depends on the architecture of the current user space
 * task (for those architectures that support both 64/32 bit compat
 * tasks).
 */
function user_long_warn:long (addr:long) {
	try {
		return user_long_error(addr)
	} catch {
		warn(sprintf("user long copy fault %p [man error::fault]",
			     addr))
		return 0
	}
}

/**
 * sfunction user_ulong_error - Retrieves a unsigned long value stored in user space
 *
 * @addr: the user space address to retrieve the unsigned long from
 *
 * Description: Returns the unsigned long value from a given user space
 * address.  If the user space data is not accessible, an error will
 * occur.  Note that the size of the unsigned long depends on the architecture
 * of the current user space task (for those architectures that
 * support both 64/32 bit compat tasks).
 */
function user_ulong_error:long (addr:long) %{ /* pure */ /* myproc-unprivileged */
#ifdef CONFIG_COMPAT
	if (_stp_is_compat_task())
		STP_GET_USER(compat_ulong_t);
	else
#endif
		STP_GET_USER(unsigned long);
	CATCH_DEREF_FAULT();
%}

/**
 * sfunction user_ulong - Retrieves an unsigned long value stored in user space
 *
 * @addr: the user space address to retrieve the unsigned long from
 *
 * Description: Returns the unsigned long value from a given user
 * space address. Returns zero when user space data is not
 * accessible. Note that the size of the unsigned long depends on the
 * architecture of the current user space task (for those
 * architectures that support both 64/32 bit compat tasks).
 */
function user_ulong:long (addr:long) {
	try { return user_ulong_error(addr) } catch { return 0 }
}

/**
 * sfunction user_ulong_warn - Retrieves an unsigned long value stored in user space
 *
 * @addr: the user space address to retrieve the unsigned long from
 *
 * Description: Returns the unsigned long value from a given user
 * space address.  Returns zero when user space data is not accessible
 * and warns about the failure (but does not error).  Note that the
 * size of the unsigned long depends on the architecture of the
 * current user space task (for those architectures that support both
 * 64/32 bit compat tasks).
 */
function user_ulong_warn:long (addr:long) {
	try {
		return user_ulong_error(addr)
	} catch {
		warn(sprintf("user unsigned long copy fault %p [man error::fault]",
			     addr))
		return 0
	}
}

/**
 * sfunction user_int8_error - Retrieves a 8-bit integer value stored in user space
 *
 * @addr: the user space address to retrieve the 8-bit integer from
 *
 * Description: Returns the 8-bit integer value from a given user
 * space address.  If the user space data is not accessible, an error
 * will occur.
 */
function user_int8_error:long (addr:long) %{ /* pure */ /* myproc-unprivileged */
	STP_GET_USER(int8_t);
	CATCH_DEREF_FAULT();
%}

/**
 * sfunction user_int8 - Retrieves a 8-bit integer value stored in user space
 *
 * @addr: the user space address to retrieve the 8-bit integer from
 *
 * Description: Returns the 8-bit integer value from a given user space
 * address.  Returns zero when user space data is not accessible.
 */
function user_int8:long (addr:long) {
	try { return user_int8_error(addr) } catch { return 0 }
}


/**
 * sfunction user_uint8_error - Retrieves a unsigned 8-bit integer value stored in user space
 *
 * @addr: the user space address to retrieve the unsigned 8-bit integer from
 *
 * Description: Returns the unsigned 8-bit integer value from a given
 * user space address.  If the user space data is not accessible, an
 * error will occur.
 */
function user_uint8_error:long (addr:long) %{ /* pure */ /* myproc-unprivileged */
	STP_GET_USER(uint8_t);
	CATCH_DEREF_FAULT();
%}

/**
 * sfunction user_uint8 - Retrieves a unsigned 8-bit integer value stored in user space
 *
 * @addr: the user space address to retrieve the unsigned 8-bit integer from
 *
 * Description: Returns the unsigned 8-bit integer value from a given
 * user space address.  Returns zero when user space data is not
 * accessible.
 */
function user_uint8:long (addr:long) {
	try { return user_uint8_error(addr) } catch { return 0 }
}

/**
 * sfunction user_int16_error - Retrieves a 16-bit integer value stored in user space
 *
 * @addr: the user space address to retrieve the 16-bit integer from
 *
 * Description: Returns the 16-bit integer value from a given user
 * space address.  If the user space data is not accessible, an error
 * will occur.
 */
function user_int16_error:long (addr:long) %{ /* pure */ /* myproc-unprivileged */
	STP_GET_USER(int16_t);
	CATCH_DEREF_FAULT();
%}

/**
 * sfunction user_int16 - Retrieves a 16-bit integer value stored in user space
 *
 * @addr: the user space address to retrieve the 16-bit integer from
 *
 * Description: Returns the 16-bit integer value from a given user space
 * address.  Returns zero when user space data is not accessible.
 */
function user_int16:long (addr:long) {
	try { return user_int16_error(addr) } catch { return 0 }
}

/**
 * sfunction user_uint16_error - Retrieves an unsigned 16-bit integer value stored in user space
 *
 * @addr: the user space address to retrieve the unsigned 16-bit integer from
 *
 * Description: Returns the unsigned 16-bit integer value from a given user
 * space address.  If the user space data is not accessible, an error
 * will occur.
 */
function user_uint16_error:long (addr:long) %{ /* pure */ /* myproc-unprivileged */
	STP_GET_USER(uint16_t);
	CATCH_DEREF_FAULT();
%}

/**
 * sfunction user_uint16 - Retrieves an unsigned 16-bit integer value stored in user space
 *
 * @addr: the user space address to retrieve the unsigned 16-bit integer from
 *
 * Description: Returns the unsigned 16-bit integer value from a given user
 * space address.  Returns zero when user space data is not accessible.
 */
function user_uint16:long (addr:long) {
	try { return user_uint16_error(addr) } catch { return 0 }
}

/**
 * sfunction user_int32_error - Retrieves a 32-bit integer value stored in user space
 *
 * @addr: the user space address to retrieve the 32-bit integer from
 *
 * Description: Returns the 32-bit integer value from a given user
 * space address.  If the user space data is not accessible, an error
 * will occur.
 */
function user_int32_error:long (addr:long) %{ /* pure */ /* myproc-unprivileged */
	STP_GET_USER(int32_t);
	CATCH_DEREF_FAULT();
%}

/**
 * sfunction user_int32 - Retrieves a 32-bit integer value stored in user space
 *
 * @addr: the user space address to retrieve the 32-bit integer from
 *
 * Description: Returns the 32-bit integer value from a given user space
 * address.  Returns zero when user space data is not accessible.
 */
function user_int32:long (addr:long) {
	try { return user_int32_error(addr) } catch { return 0 }
}

/**
 * sfunction user_uint32_error - Retrieves an unsigned 32-bit integer value stored in user space
 *
 * @addr: the user space address to retrieve the unsigned 32-bit integer from
 *
 * Description: Returns the unsigned 32-bit integer value from a given
 * user space address.  If the user space data is not accessible, an
 * error will occur.
 */
function user_uint32_error:long (addr:long) %{ /* pure */ /* myproc-unprivileged */
	STP_GET_USER(uint32_t);
	CATCH_DEREF_FAULT();
%}

/**
 * sfunction user_uint32 - Retrieves an unsigned 32-bit integer value stored in user space
 *
 * @addr: the user space address to retrieve the unsigned 32-bit integer from
 *
 * Description: Returns the unsigned 32-bit integer value from a given user
 * space address.  Returns zero when user space data is not accessible.
 */
function user_uint32:long (addr:long) {
	try { return user_uint32_error(addr) } catch { return 0 }
}

/**
 * sfunction user_int64_error - Retrieves a 64-bit integer value stored in user space
 *
 * @addr: the user space address to retrieve the 64-bit integer from
 *
 * Description: Returns the 64-bit integer value from a given user
 * space address.  If the user space data is not accessible, an error
 * will occur.
 */
function user_int64_error:long (addr:long) %{ /* pure */ /* myproc-unprivileged */
	STP_GET_USER(int64_t);
	CATCH_DEREF_FAULT();
%}

/**
 * sfunction user_int64 - Retrieves a 64-bit integer value stored in user space
 *
 * @addr: the user space address to retrieve the 64-bit integer from
 *
 * Description: Returns the 64-bit integer value from a given user space
 * address.  Returns zero when user space data is not accessible.
 */
function user_int64:long (addr:long) {
	try { return user_int64_error(addr) } catch { return 0 }
}

/**
 * sfunction user_uint64_error - Retrieves an unsigned 64-bit integer value stored in user space
 *
 * @addr: the user space address to retrieve the unsigned 64-bit integer from
 *
 * Description: Returns the unsigned 64-bit integer value from a given
 * user space address.  If the user space data is not accessible, an
 * error will occur.
 */
function user_uint64_error:long (addr:long) {
	/* NB: We have no script-level notion of 64-bit unsigned, but
	 * we keep user_uint64_error anyway for API completeness. */
	return user_int64_error(addr)
}

/**
 * sfunction user_uint64 - Retrieves an unsigned 64-bit integer value stored in user space
 *
 * @addr: the user space address to retrieve the unsigned 64-bit integer from
 *
 * Description: Returns the unsigned 64-bit integer value from a given user
 * space address.  Returns zero when user space data is not accessible.
 */
function user_uint64:long (addr:long) {
	/* NB: We have no script-level notion of 64-bit unsigned,
	 * but we keep user_uint64 anyway for API completeness. */
	return user_int64(addr)
}

/**
 * sfunction user_buffer_quoted - Retrieves and quotes buffer from user space
 *
 * @addr: the user space address to retrieve the buffer from
 * @inlen: the exact length of the buffer to read
 * @outlen: the maximum length of the output string
 *
 * Description: Reads inlen characters of a buffer from the given user space
 * memory address, and returns up to outlen characters, where any ASCII
 * characters that are not printable are replaced by the corresponding escape
 * sequence in the returned string. Note that the string will be surrounded by
 * double quotes. On the rare cases when user space data is not accessible at
 * the given address, the address itself is returned as a string, without
 * double quotes.
 */
function user_buffer_quoted:string (addr:long, inlen:long, outlen:long)
%{
  size_t outlen = (size_t)clamp_t(int, STAP_ARG_outlen, 0, MAXSTRINGLEN);
  if (outlen == 0)
    return;
  if ( _stp_text_str(STAP_RETVALUE, (char *)(uintptr_t)STAP_ARG_addr,
	             STAP_ARG_inlen, outlen, 1, 1, 1) < 0)
    snprintf(STAP_RETVALUE, outlen, "0x%lx",
	     (unsigned long)(void *)(uintptr_t)STAP_ARG_addr);
%}

/**
 * sfunction user_buffer_quoted_error - Retrieves and quotes buffer from user space
 *
 * @addr: the user space address to retrieve the buffer from
 * @inlen: the exact length of the buffer to read
 * @outlen: the maximum length of the output string
 *
 * Description: Reads inlen characters of a buffer from the given user space
 * memory address, and returns up to outlen characters, where any ASCII
 * characters that are not printable are replaced by the corresponding escape
 * sequence in the returned string. Note that the string will be surrounded by
 * double quotes. On the rare cases when user space data is not accessible at
 * the given address, an error is thrown.
 */
function user_buffer_quoted_error:string (addr:long, inlen:long, outlen:long)
%{
  size_t outlen = (size_t)clamp_t(int, STAP_ARG_outlen, 0, MAXSTRINGLEN);
  if (outlen == 0
      || _stp_text_str(STAP_RETVALUE,
		       (char *) (uintptr_t) STAP_ARG_addr,
		       STAP_ARG_inlen, outlen, 1, 1, 1) < 0)
    STAP_ERROR("Unable to access user space data at 0x%lx",
	       (unsigned long)(void *)(uintptr_t)STAP_ARG_addr);
%}
