File 2921-erts-Implement-erlang-display_string-2-used-for-test.patch of Package erlang
From 3e4baaee6543befa35e604b9d40391c719d0d752 Mon Sep 17 00:00:00 2001
From: Lukas Larsson <lukas@erlang.org>
Date: Thu, 12 May 2022 13:55:12 +0200
Subject: [PATCH 01/34] erts: Implement erlang:display_string/2 used for
testing
---
erts/emulator/beam/bif.c | 83 ++++++++++++++++++++-----
erts/emulator/beam/bif.tab | 2 +-
erts/emulator/beam/erl_dirty_bif.tab | 1 +
erts/emulator/beam/sys.h | 2 +-
erts/emulator/test/exception_SUITE.erl | 2 +
erts/preloaded/ebin/erlang.beam | Bin 132536 -> 132836 bytes
erts/preloaded/src/erlang.erl | 15 ++++-
lib/kernel/src/erl_erts_errors.erl | 30 ++++++++-
8 files changed, 116 insertions(+), 19 deletions(-)
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index 1d0a145f3f..c8e7caac15 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -23,6 +23,10 @@
#endif
#include <stddef.h> /* offsetof() */
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#define WANT_NONBLOCKING
#include "sys.h"
#include "erl_vm.h"
#include "erl_sys_driver.h"
@@ -4180,27 +4184,78 @@ BIF_RETTYPE erts_debug_display_1(BIF_ALIST_1)
BIF_RET(res);
}
-
-BIF_RETTYPE display_string_1(BIF_ALIST_1)
+BIF_RETTYPE display_string_2(BIF_ALIST_2)
{
Process* p = BIF_P;
- Eterm string = BIF_ARG_1;
- Sint len = erts_unicode_list_to_buf_len(string);
+ Eterm string = BIF_ARG_2;
+ Sint len;
Sint written;
byte *str;
- int res;
+ int res, fd;
+ byte *temp_alloc = NULL;
+
+ if (ERTS_IS_ATOM_STR("stdout", BIF_ARG_1)) {
+ fd = fileno(stdout);
+ } else if (ERTS_IS_ATOM_STR("stderr", BIF_ARG_1)) {
+ fd = fileno(stderr);
+#if defined(HAVE_SYS_IOCTL_H) && defined(TIOCSTI)
+ } else if (ERTS_IS_ATOM_STR("stdin", BIF_ARG_1)) {
+ fd = open("/proc/self/fd/0",0);
+#endif
+ } else {
+ BIF_ERROR(p, BADARG);
+ }
+ if (is_list(string) || is_nil(string)) {
+ len = erts_unicode_list_to_buf_len(string);
+ if (len < 0) BIF_ERROR(p, BADARG);
+ str = temp_alloc = (byte *) erts_alloc(ERTS_ALC_T_TMP, sizeof(char)*len);
+ res = erts_unicode_list_to_buf(string, str, len, &written);
+ if (res != 0 || written != len)
+ erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error (%d)\n", __FILE__, __LINE__, res);
+ } else if (is_binary(string)) {
+ Uint bitoffs, bitsize;
+ ERTS_GET_BINARY_BYTES(string, str, bitoffs, bitsize);
+ if (bitsize % 8 != 0) BIF_ERROR(p, BADARG);
+ len = binary_size(string);
+ if (bitoffs != 0) {
+ str = erts_get_aligned_binary_bytes(string, &temp_alloc);
+ }
+ } else {
+ BIF_ERROR(p, BADARG);
+ }
- if (len < 0) {
- BIF_ERROR(p, BADARG);
+#if defined(HAVE_SYS_IOCTL_H) && defined(TIOCSTI)
+ if (ERTS_IS_ATOM_STR("stdin", BIF_ARG_1)) {
+ for (int i = 0; i < len; i++) {
+ if (ioctl(fd, TIOCSTI, str+i) < 0) {
+ fprintf(stderr,"failed to write to %s (%s)\r\n", "/proc/self/fd/0",
+ strerror(errno));
+ close(fd);
+ goto error;
+ }
+ }
+ close(fd);
+ } else
+#endif
+ {
+ written = 0;
+ do {
+ res = write(fd, str+written, len-written);
+ if (res < 0 && errno != ERRNO_BLOCK && errno != EINTR)
+ goto error;
+ written += res;
+ } while (written < len);
}
- str = (byte *) erts_alloc(ERTS_ALC_T_TMP, sizeof(char)*(len + 1));
- res = erts_unicode_list_to_buf(string, str, len, &written);
- if (res != 0 || written != len)
- erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error (%d)\n", __FILE__, __LINE__, res);
- str[len] = '\0';
- erts_fprintf(stderr, "%s", str);
- erts_free(ERTS_ALC_T_TMP, (void *) str);
+ if (temp_alloc)
+ erts_free(ERTS_ALC_T_TMP, (void *) temp_alloc);
BIF_RET(am_true);
+
+error: {
+ char *errnostr = erl_errno_id(errno);
+ BIF_P->fvalue = am_atom_put(errnostr, strlen(errnostr));
+ erts_free(ERTS_ALC_T_TMP, (void *) str);
+ BIF_ERROR(p, BADARG | EXF_HAS_EXT_INFO);
+ }
}
BIF_RETTYPE display_nl_0(BIF_ALIST_0)
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index dffe3963b5..17f35a1bc7 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -56,7 +56,7 @@ bif erlang:crc32_combine/3
bif erlang:date/0
bif erlang:delete_module/1
bif erlang:display/1
-bif erlang:display_string/1
+bif erlang:display_string/2
bif erlang:display_nl/0
ubif erlang:element/2
bif erlang:erase/0
diff --git a/erts/emulator/beam/erl_dirty_bif.tab b/erts/emulator/beam/erl_dirty_bif.tab
index 3f16f3e0f3..9245a19be6 100644
--- a/erts/emulator/beam/erl_dirty_bif.tab
+++ b/erts/emulator/beam/erl_dirty_bif.tab
@@ -50,6 +50,7 @@ dirty-io erts_debug:dirty_io/2
dirty-cpu erts_debug:lcnt_control/2
dirty-cpu erts_debug:lcnt_collect/0
dirty-cpu erts_debug:lcnt_clear/0
+dirty-cpu erlang:display_string/2
# --- TEST of Dirty BIF functionality ---
# Functions below will execute on dirty schedulers when emulator has
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 20b0571e43..b57cfd6952 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -608,7 +608,7 @@ extern erts_tsd_key_t erts_is_crash_dumping_key;
static unsigned long zero_value = 0, one_value = 1;
# define SET_BLOCKING(fd) { if (ioctlsocket((fd), FIONBIO, &zero_value) != 0) fprintf(stderr, "Error setting socket to non-blocking: %d\n", WSAGetLastError()); }
# define SET_NONBLOCKING(fd) ioctlsocket((fd), FIONBIO, &one_value)
-
+# define ERRNO_BLOCK EAGAIN /* We use the posix way for windows */
# else
# ifdef NB_FIONBIO /* Old BSD */
# include <sys/ioctl.h>
diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl
index 5cce5d1491..dfec2a28dc 100644
--- a/erts/emulator/test/exception_SUITE.erl
+++ b/erts/emulator/test/exception_SUITE.erl
@@ -851,6 +851,8 @@ error_info(_Config) ->
{display, ["test erlang:display/1"], [no_fail]},
{display_string, [{a,b,c}]},
+ {display_string, [standard_out,"test erlang:display/2"]},
+ {display_string, [stdout,{a,b,c}]},
%% Internal undcoumented BIFs.
{dist_ctrl_get_data, 1},
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 8afcfd37ad..7c6d2376f4 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -216,7 +216,7 @@
-export([crc32/2, crc32_combine/3, date/0, decode_packet/3]).
-export([delete_element/2]).
-export([delete_module/1, demonitor/1, demonitor/2, display/1]).
--export([display_nl/0, display_string/1, erase/0, erase/1]).
+-export([display_nl/0, display_string/1, display_string/2, erase/0, erase/1]).
-export([error/1, error/2, error/3, exit/1, exit/2, exit_signal/2, external_size/1]).
-export([external_size/2, finish_after_on_load/2, finish_loading/1, float/1]).
-export([float_to_binary/1, float_to_binary/2,
@@ -850,8 +850,19 @@ display_nl() ->
%% display_string/1
-spec erlang:display_string(P1) -> true when
+ P1 :: string() | binary().
+display_string(String) ->
+ try erlang:display_string(stderr, String)
+ catch error:badarg:ST ->
+ [{erlang, display_string, _, [ErrorInfo]}|_] = ST,
+ erlang:error(badarg, [String], [ErrorInfo])
+ end.
+
+%% display_string/2
+-spec erlang:display_string(Device, P1) -> true when
+ Device :: stdin | stdout | stderr,
P1 :: string().
-display_string(_P1) ->
+display_string(_Stream,_P1) ->
erlang:nif_error(undefined).
%% dt_append_vm_tag_data/1
diff --git a/lib/kernel/src/erl_erts_errors.erl b/lib/kernel/src/erl_erts_errors.erl
index 4d23112b83..fe7758dae0 100644
--- a/lib/kernel/src/erl_erts_errors.erl
+++ b/lib/kernel/src/erl_erts_errors.erl
@@ -352,8 +352,19 @@ format_erlang_error(demonitor, [_], _) ->
format_erlang_error(demonitor, [Ref,Options], _) ->
Arg1 = must_be_ref(Ref),
[Arg1,maybe_option_list_error(Options, Arg1)];
-format_erlang_error(display_string, [_], _) ->
+format_erlang_error(display_string, [_], none) ->
[not_string];
+format_erlang_error(display_string, [_], Cause) ->
+ maybe_posix_message(Cause, false);
+format_erlang_error(display_string, [Device, _], none) ->
+ case lists:member(Device,[stdin,stdout,stderr]) of
+ true ->
+ [[],not_string];
+ false ->
+ [not_device,[]]
+ end;
+format_erlang_error(display_string, [_, _], Cause) ->
+ maybe_posix_message(Cause, true);
format_erlang_error(element, [Index, Tuple], _) ->
[if
not is_integer(Index) ->
@@ -1430,8 +1441,23 @@ is_flat_char_list([H|T]) ->
is_flat_char_list([]) -> true;
is_flat_char_list(_) -> false.
+maybe_posix_message(Cause, HasDevice) ->
+ case erl_posix_msg:message(Cause) of
+ "unknown POSIX error" ->
+ unknown;
+ PosixStr when HasDevice ->
+ [unicode:characters_to_binary(
+ io_lib:format("~ts (~tp)",[PosixStr, Cause]))];
+ PosixStr when not HasDevice ->
+ [{general,
+ unicode:characters_to_binary(
+ io_lib:format("~ts (~tp)",[PosixStr, Cause]))}]
+ end.
+
format_error_map([""|Es], ArgNum, Map) ->
format_error_map(Es, ArgNum + 1, Map);
+format_error_map([{general, E}|Es], ArgNum, Map) ->
+ format_error_map(Es, ArgNum, Map#{ general => expand_error(E)});
format_error_map([E|Es], ArgNum, Map) ->
format_error_map(Es, ArgNum + 1, Map#{ArgNum => expand_error(E)});
format_error_map([], _, Map) ->
@@ -1519,6 +1545,8 @@ expand_error(not_ref) ->
<<"not a reference">>;
expand_error(not_string) ->
<<"not a list of characters">>;
+expand_error(not_device) ->
+ <<"not a valid device type">>;
expand_error(not_tuple) ->
<<"not a tuple">>;
expand_error(range) ->
--
2.35.3