aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/emulator/beam/beam_ranges.c22
-rw-r--r--erts/emulator/beam/break.c51
-rw-r--r--erts/emulator/beam/erl_alloc.types1
-rw-r--r--erts/emulator/beam/erl_process_dump.c363
-rw-r--r--erts/emulator/beam/global.h2
-rw-r--r--erts/emulator/beam/module.c1
-rw-r--r--erts/include/internal/erl_printf.h1
-rw-r--r--erts/lib_src/common/erl_printf.c12
-rw-r--r--lib/observer/src/crashdump_viewer.erl93
-rw-r--r--lib/observer/test/crashdump_helper.erl40
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl47
-rw-r--r--lib/public_key/src/public_key.erl26
-rw-r--r--lib/public_key/test/public_key_SUITE.erl26
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ec_key_param0.pem28
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ec_key_param1.pem25
-rw-r--r--lib/ssl/src/inet_tls_dist.erl2
-rw-r--r--lib/stdlib/src/filename.erl17
-rw-r--r--lib/stdlib/test/filename_SUITE.erl120
-rw-r--r--lib/wx/c_src/wxe_driver.c23
19 files changed, 851 insertions, 49 deletions
diff --git a/erts/emulator/beam/beam_ranges.c b/erts/emulator/beam/beam_ranges.c
index 6e373a3480..01bda7f3c1 100644
--- a/erts/emulator/beam/beam_ranges.c
+++ b/erts/emulator/beam/beam_ranges.c
@@ -32,6 +32,15 @@ typedef struct {
erts_atomic_t end; /* (BeamInstr*) Points one word beyond last function in module. */
} Range;
+/*
+ * Used for crash dumping of literals. The size of erts_dump_lit_areas is
+ * always twice the number of active ranges (to allow for literals in both
+ * current and old code).
+ */
+
+ErtsLiteralArea** erts_dump_lit_areas;
+Uint erts_dump_num_lit_areas;
+
/* Range 'end' needs to be atomic as we purge module
by setting end=start in active code_ix */
#define RANGE_END(R) ((BeamInstr*)erts_atomic_read_nob(&(R)->end))
@@ -97,6 +106,11 @@ erts_init_ranges(void)
r[i].allocated = 0;
erts_atomic_init_nob(&r[i].mid, 0);
}
+
+ erts_dump_num_lit_areas = 8;
+ erts_dump_lit_areas = (ErtsLiteralArea **)
+ erts_alloc(ERTS_ALC_T_CRASH_DUMP,
+ erts_dump_num_lit_areas * sizeof(ErtsLiteralArea*));
}
void
@@ -164,6 +178,14 @@ erts_end_staging_ranges(int commit)
erts_atomic_set_nob(&r[dst].mid,
(erts_aint_t) (r[dst].modules +
r[dst].n / 2));
+
+ if (r[dst].allocated * 2 > erts_dump_num_lit_areas) {
+ erts_dump_num_lit_areas *= 2;
+ erts_dump_lit_areas = (ErtsLiteralArea **)
+ erts_realloc(ERTS_ALC_T_CRASH_DUMP,
+ (void *) erts_dump_lit_areas,
+ erts_dump_num_lit_areas * sizeof(ErtsLiteralArea*));
+ }
}
}
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index 35b2365655..d9ee940662 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -58,6 +58,8 @@ static void dump_attributes(fmtfn_t to, void *to_arg, byte* ptr, int size);
extern char* erts_system_version[];
+#define WRITE_BUFFER_SIZE (64*1024)
+
static void
port_info(fmtfn_t to, void *to_arg)
{
@@ -673,18 +675,28 @@ bin_check(void)
static Sint64 crash_dump_limit = ERTS_SINT64_MAX;
static Sint64 crash_dump_written = 0;
-static int crash_dump_limited_writer(void* vfdp, char* buf, size_t len)
+typedef struct LimitedWriterInfo_ {
+ fmtfn_t to;
+ void* to_arg;
+} LimitedWriterInfo;
+
+static int
+crash_dump_limited_writer(void* vfdp, char* buf, size_t len)
{
const char stop_msg[] = "\n=abort:CRASH DUMP SIZE LIMIT REACHED\n";
+ LimitedWriterInfo* lwi = (LimitedWriterInfo *) vfdp;
crash_dump_written += len;
if (crash_dump_written <= crash_dump_limit) {
- return erts_write_fd(vfdp, buf, len);
+ return lwi->to(lwi->to_arg, buf, len);
}
len -= (crash_dump_written - crash_dump_limit);
- erts_write_fd(vfdp, buf, len);
- erts_write_fd(vfdp, (char*)stop_msg, sizeof(stop_msg)-1);
+ lwi->to(lwi->to_arg, buf, len);
+ lwi->to(lwi->to_arg, (char*)stop_msg, sizeof(stop_msg)-1);
+ if (lwi->to == &erts_write_fp) {
+ fclose((FILE *) lwi->to_arg);
+ }
/* We assume that crash dump was called from erts_exit_vv() */
erts_exit_epilogue();
@@ -707,6 +719,9 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
int i;
fmtfn_t to = &erts_write_fd;
void* to_arg;
+ FILE* fp = 0;
+ LimitedWriterInfo lwi;
+ static char* write_buffer; /* 'static' to avoid a leak warning in valgrind */
if (ERTS_SOMEONE_IS_CRASH_DUMPING)
return;
@@ -810,9 +825,30 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
fd = open(dumpname,O_WRONLY | O_CREAT | O_TRUNC,0640);
if (fd < 0)
return; /* Can't create the crash dump, skip it */
- to_arg = (void*)&fd;
+
+ /*
+ * Wrap into a FILE* so that we can use buffered output. Set an
+ * explicit buffer to make sure the first write does not fail because
+ * of a failure to allocate a buffer.
+ */
+ write_buffer = (char *) erts_alloc_fnf(ERTS_ALC_T_TMP, WRITE_BUFFER_SIZE);
+ if (write_buffer && (fp = fdopen(fd, "w")) != NULL) {
+ setvbuf(fp, write_buffer, _IOFBF, WRITE_BUFFER_SIZE);
+ lwi.to = &erts_write_fp;
+ lwi.to_arg = (void*)fp;
+ } else {
+ lwi.to = &erts_write_fd;
+ lwi.to_arg = (void*)&fd;
+ }
+ if (to == &crash_dump_limited_writer) {
+ to_arg = (void *) &lwi;
+ } else {
+ to = lwi.to;
+ to_arg = lwi.to_arg;
+ }
+
time(&now);
- erts_cbprintf(to, to_arg, "=erl_crash_dump:0.3\n%s", ctime(&now));
+ erts_cbprintf(to, to_arg, "=erl_crash_dump:0.4\n%s", ctime(&now));
if (file != NULL)
erts_cbprintf(to, to_arg, "The error occurred in file %s, line %d\n", file, line);
@@ -916,6 +952,9 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
}
erts_cbprintf(to, to_arg, "=end\n");
+ if (fp) {
+ fclose(fp);
+ }
close(fd);
erts_fprintf(stderr,"done\n");
}
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index 878b971b07..2960272eab 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -260,6 +260,7 @@ type MREF_TAB LONG_LIVED SYSTEM magic_ref_table
type MINDIRECTION FIXED_SIZE SYSTEM magic_indirection
type BINARY_FIND SHORT_LIVED PROCESSES binary_find
type OPEN_PORT_ENV TEMPORARY SYSTEM open_port_env
+type CRASH_DUMP STANDARD SYSTEM crash_dump
type THR_Q_EL STANDARD SYSTEM thr_q_element
type THR_Q_EL_SL FIXED_SIZE SYSTEM sl_thr_q_element
diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c
index 5a2c262ff1..12ef4aab8a 100644
--- a/erts/emulator/beam/erl_process_dump.c
+++ b/erts/emulator/beam/erl_process_dump.c
@@ -32,6 +32,7 @@
#include "dist.h"
#include "beam_catches.h"
#include "erl_binary.h"
+#include "erl_map.h"
#define ERTS_WANT_EXTERNAL_TAGS
#include "external.h"
@@ -51,6 +52,11 @@ static void print_function_from_pc(fmtfn_t to, void *to_arg, BeamInstr* x);
static void heap_dump(fmtfn_t to, void *to_arg, Eterm x);
static void dump_binaries(fmtfn_t to, void *to_arg, Binary* root);
static void dump_externally(fmtfn_t to, void *to_arg, Eterm term);
+static void mark_literal(Eterm* ptr);
+static void init_literal_areas(void);
+static void dump_literals(fmtfn_t to, void *to_arg);
+static void dump_module_literals(fmtfn_t to, void *to_arg,
+ ErtsLiteralArea* lit_area);
static Binary* all_binaries;
@@ -58,14 +64,14 @@ extern BeamInstr beam_apply[];
extern BeamInstr beam_exit[];
extern BeamInstr beam_continue_exit[];
-
void
erts_deep_process_dump(fmtfn_t to, void *to_arg)
{
int i, max = erts_ptab_max(&erts_proc);
all_binaries = NULL;
-
+ init_literal_areas();
+
for (i = 0; i < max; i++) {
Process *p = erts_pix2proc(i);
if (p && p->i != ENULL) {
@@ -75,6 +81,7 @@ erts_deep_process_dump(fmtfn_t to, void *to_arg)
}
}
+ dump_literals(to, to_arg);
dump_binaries(to, to_arg, all_binaries);
}
@@ -373,7 +380,9 @@ heap_dump(fmtfn_t to, void *to_arg, Eterm x)
next = (Eterm *) x;
} else if (is_list(x)) {
ptr = list_val(x);
- if (ptr[0] != OUR_NIL) {
+ if (erts_is_literal(x, ptr)) {
+ mark_literal(ptr);
+ } else if (ptr[0] != OUR_NIL) {
erts_print(to, to_arg, PTR_FMT ":l", ptr);
dump_element(to, to_arg, ptr[0]);
erts_putc(to, to_arg, '|');
@@ -392,7 +401,9 @@ heap_dump(fmtfn_t to, void *to_arg, Eterm x)
ptr = boxed_val(x);
hdr = *ptr;
- if (hdr != OUR_NIL) { /* If not visited */
+ if (erts_is_literal(x, ptr)) {
+ mark_literal(ptr);
+ } else if (hdr != OUR_NIL) {
erts_print(to, to_arg, PTR_FMT ":", ptr);
if (is_arity_value(hdr)) {
Uint i;
@@ -498,11 +509,77 @@ heap_dump(fmtfn_t to, void *to_arg, Eterm x)
erts_print(to, to_arg, "p<%beu.%beu>\n",
port_channel_no(x), port_number(x));
*ptr = OUR_NIL;
+ } else if (is_map_header(hdr)) {
+ if (is_flatmap_header(hdr)) {
+ flatmap_t* fmp = (flatmap_t *) flatmap_val(x);
+ Eterm* values = ptr + sizeof(flatmap_t) / sizeof(Eterm);
+ Uint map_size = fmp->size;
+ int i;
+
+ erts_print(to, to_arg, "Mf" ETERM_FMT ":", map_size);
+ dump_element(to, to_arg, fmp->keys);
+ erts_putc(to, to_arg, ':');
+ for (i = 0; i < map_size; i++) {
+ dump_element(to, to_arg, values[i]);
+ if (is_immed(values[i])) {
+ values[i] = make_small(0);
+ }
+ if (i < map_size-1) {
+ erts_putc(to, to_arg, ',');
+ }
+ }
+ erts_putc(to, to_arg, '\n');
+ *ptr = OUR_NIL;
+ x = fmp->keys;
+ if (map_size) {
+ fmp->keys = (Eterm) next;
+ next = &values[map_size-1];
+ }
+ continue;
+ } else {
+ Uint i;
+ Uint sz = 0;
+ Eterm* nodes = ptr + 1;
+
+ switch (MAP_HEADER_TYPE(hdr)) {
+ case MAP_HEADER_TAG_HAMT_HEAD_ARRAY:
+ nodes++;
+ sz = 16;
+ erts_print(to, to_arg, "Mh" ETERM_FMT ":" ETERM_FMT ":",
+ hashmap_size(x), sz);
+ break;
+ case MAP_HEADER_TAG_HAMT_HEAD_BITMAP:
+ nodes++;
+ sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+ erts_print(to, to_arg, "Mh" ETERM_FMT ":" ETERM_FMT ":",
+ hashmap_size(x), sz);
+ break;
+ case MAP_HEADER_TAG_HAMT_NODE_BITMAP:
+ sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
+ erts_print(to, to_arg, "Mn" ETERM_FMT ":", sz);
+ break;
+ }
+ *ptr = OUR_NIL;
+ for (i = 0; i < sz; i++) {
+ dump_element(to, to_arg, nodes[i]);
+ if (is_immed(nodes[i])) {
+ nodes[i] = make_small(0);
+ }
+ if (i < sz-1) {
+ erts_putc(to, to_arg, ',');
+ }
+ }
+ erts_putc(to, to_arg, '\n');
+ x = nodes[0];
+ nodes[0] = (Eterm) next;
+ next = &nodes[sz-1];
+ continue;
+ }
} else {
/*
* All other we dump in the external term format.
*/
- dump_externally(to, to_arg, x);
+ dump_externally(to, to_arg, x);
erts_putc(to, to_arg, '\n');
*ptr = OUR_NIL;
}
@@ -564,11 +641,6 @@ dump_externally(fmtfn_t to, void *to_arg, Eterm term)
}
}
- /* Do not handle maps */
- if (is_map(term)) {
- term = am_undefined;
- }
-
s = p = sbuf;
erts_encode_ext(term, &p);
erts_print(to, to_arg, "E%X:", p-s);
@@ -577,6 +649,277 @@ dump_externally(fmtfn_t to, void *to_arg, Eterm term)
}
}
+/*
+ * Handle dumping of literal areas.
+ */
+
+static ErtsLiteralArea** lit_areas;
+static Uint num_lit_areas;
+
+static int compare_areas(const void * a, const void * b)
+{
+ ErtsLiteralArea** a_p = (ErtsLiteralArea **) a;
+ ErtsLiteralArea** b_p = (ErtsLiteralArea **) b;
+
+ if (*a_p < *b_p) {
+ return -1;
+ } else if (*b_p < *a_p) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+static void
+init_literal_areas(void)
+{
+ int i;
+ Module* modp;
+ ErtsCodeIndex code_ix;
+ ErtsLiteralArea** area_p;
+
+ code_ix = erts_active_code_ix();
+ erts_rlock_old_code(code_ix);
+
+ lit_areas = area_p = erts_dump_lit_areas;
+ num_lit_areas = 0;
+ for (i = 0; i < module_code_size(code_ix); i++) {
+ modp = module_code(i, code_ix);
+ if (modp == NULL) {
+ continue;
+ }
+ if (modp->curr.code_length > 0 &&
+ modp->curr.code_hdr->literal_area) {
+ *area_p++ = modp->curr.code_hdr->literal_area;
+ }
+ if (modp->old.code_length > 0 && modp->old.code_hdr->literal_area) {
+ *area_p++ = modp->old.code_hdr->literal_area;
+ }
+ }
+
+ num_lit_areas = area_p - lit_areas;
+ ASSERT(num_lit_areas <= erts_dump_num_lit_areas);
+ for (i = 0; i < num_lit_areas; i++) {
+ lit_areas[i]->off_heap = 0;
+ }
+
+ qsort(lit_areas, num_lit_areas, sizeof(ErtsLiteralArea *),
+ compare_areas);
+
+ erts_runlock_old_code(code_ix);
+}
+
+static int search_areas(const void * a, const void * b) {
+ Eterm* key = (Eterm *) a;
+ ErtsLiteralArea** b_p = (ErtsLiteralArea **) b;
+ if (key < b_p[0]->start) {
+ return -1;
+ } else if (b_p[0]->end <= key) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void mark_literal(Eterm* ptr)
+{
+ ErtsLiteralArea** ap;
+
+ ap = bsearch(ptr, lit_areas, num_lit_areas, sizeof(ErtsLiteralArea*),
+ search_areas);
+ ASSERT(ap);
+ ap[0]->off_heap = (struct erl_off_heap_header *) 1;
+}
+
+
+static void
+dump_literals(fmtfn_t to, void *to_arg)
+{
+ ErtsCodeIndex code_ix;
+ int i;
+
+ code_ix = erts_active_code_ix();
+ erts_rlock_old_code(code_ix);
+
+ erts_print(to, to_arg, "=literals\n");
+ for (i = 0; i < num_lit_areas; i++) {
+ if (lit_areas[i]->off_heap) {
+ dump_module_literals(to, to_arg, lit_areas[i]);
+ }
+ }
+
+ erts_runlock_old_code(code_ix);
+}
+
+static void
+dump_module_literals(fmtfn_t to, void *to_arg, ErtsLiteralArea* lit_area)
+{
+ Eterm* htop;
+ Eterm* hend;
+
+ htop = lit_area->start;
+ hend = lit_area->end;
+ while (htop < hend) {
+ Eterm w = *htop;
+ Eterm term;
+ Uint size;
+
+ switch (primary_tag(w)) {
+ case TAG_PRIMARY_HEADER:
+ term = make_boxed(htop);
+ erts_print(to, to_arg, PTR_FMT ":", htop);
+ if (is_arity_value(w)) {
+ Uint i;
+ Uint arity = arityval(w);
+
+ erts_print(to, to_arg, "t" ETERM_FMT ":", arity);
+ for (i = 1; i <= arity; i++) {
+ dump_element(to, to_arg, htop[i]);
+ if (i < arity) {
+ erts_putc(to, to_arg, ',');
+ }
+ }
+ erts_putc(to, to_arg, '\n');
+ } else if (w == HEADER_FLONUM) {
+ FloatDef f;
+ char sbuf[31];
+ int i;
+
+ GET_DOUBLE_DATA((htop+1), f);
+ i = sys_double_to_chars(f.fd, sbuf, sizeof(sbuf));
+ sys_memset(sbuf+i, 0, 31-i);
+ erts_print(to, to_arg, "F%X:%s\n", i, sbuf);
+ } else if (_is_bignum_header(w)) {
+ erts_print(to, to_arg, "B%T\n", term);
+ } else if (is_binary_header(w)) {
+ Uint tag = thing_subtag(w);
+ Uint size = binary_size(term);
+ Uint i;
+
+ if (tag == HEAP_BINARY_SUBTAG) {
+ byte* p;
+
+ erts_print(to, to_arg, "Yh%X:", size);
+ p = binary_bytes(term);
+ for (i = 0; i < size; i++) {
+ erts_print(to, to_arg, "%02X", p[i]);
+ }
+ } else if (tag == REFC_BINARY_SUBTAG) {
+ ProcBin* pb = (ProcBin *) binary_val(term);
+ Binary* val = pb->val;
+
+ if (erts_atomic_xchg_nob(&val->intern.refc, 0) != 0) {
+ val->intern.flags = (UWord) all_binaries;
+ all_binaries = val;
+ }
+ erts_print(to, to_arg,
+ "Yc" PTR_FMT ":" PTR_FMT ":" PTR_FMT,
+ val,
+ pb->bytes - (byte *)val->orig_bytes,
+ size);
+ } else if (tag == SUB_BINARY_SUBTAG) {
+ ErlSubBin* Sb = (ErlSubBin *) binary_val(term);
+ Eterm* real_bin;
+ void* val;
+
+ real_bin = boxed_val(Sb->orig);
+ if (thing_subtag(*real_bin) == REFC_BINARY_SUBTAG) {
+ /*
+ * Unvisited REFC_BINARY: Point directly to
+ * the binary.
+ */
+ ProcBin* pb = (ProcBin *) real_bin;
+ val = pb->val;
+ } else {
+ /*
+ * Heap binary or visited REFC binary: Point
+ * to heap binary or ProcBin on the heap.
+ */
+ val = real_bin;
+ }
+ erts_print(to, to_arg,
+ "Ys" PTR_FMT ":" PTR_FMT ":" PTR_FMT,
+ val, Sb->offs, size);
+ }
+ erts_putc(to, to_arg, '\n');
+ } else if (is_map_header(w)) {
+ if (is_flatmap_header(w)) {
+ flatmap_t* fmp = (flatmap_t *) flatmap_val(term);
+ Eterm* values = htop + sizeof(flatmap_t) / sizeof(Eterm);
+ Uint map_size = fmp->size;
+ int i;
+
+ erts_print(to, to_arg, "Mf" ETERM_FMT ":", map_size);
+ dump_element(to, to_arg, fmp->keys);
+ erts_putc(to, to_arg, ':');
+ for (i = 0; i < map_size; i++) {
+ dump_element(to, to_arg, values[i]);
+ if (i < map_size-1) {
+ erts_putc(to, to_arg, ',');
+ }
+ }
+ erts_putc(to, to_arg, '\n');
+ } else {
+ Uint i;
+ Uint sz = 0;
+ Eterm* nodes = htop + 1;
+
+ switch (MAP_HEADER_TYPE(w)) {
+ case MAP_HEADER_TAG_HAMT_HEAD_ARRAY:
+ nodes++;
+ sz = 16;
+ erts_print(to, to_arg, "Mh" ETERM_FMT ":" ETERM_FMT ":",
+ hashmap_size(term), sz);
+ break;
+ case MAP_HEADER_TAG_HAMT_HEAD_BITMAP:
+ nodes++;
+ sz = hashmap_bitcount(MAP_HEADER_VAL(w));
+ erts_print(to, to_arg, "Mh" ETERM_FMT ":" ETERM_FMT ":",
+ hashmap_size(term), sz);
+ break;
+ case MAP_HEADER_TAG_HAMT_NODE_BITMAP:
+ sz = hashmap_bitcount(MAP_HEADER_VAL(w));
+ erts_print(to, to_arg, "Mn" ETERM_FMT ":", sz);
+ break;
+ }
+ for (i = 0; i < sz; i++) {
+ dump_element(to, to_arg, nodes[i]);
+ if (i < sz-1) {
+ erts_putc(to, to_arg, ',');
+ }
+ }
+ erts_putc(to, to_arg, '\n');
+ }
+ }
+ size = 1 + header_arity(w);
+ switch (w & _HEADER_SUBTAG_MASK) {
+ case MAP_SUBTAG:
+ if (is_flatmap_header(w)) {
+ size += 1 + flatmap_get_size(htop);
+ } else {
+ size += hashmap_bitcount(MAP_HEADER_VAL(w));
+ }
+ break;
+ case SUB_BINARY_SUBTAG:
+ size += 1;
+ break;
+ }
+ break;
+ default:
+ ASSERT(!is_header(htop[1]));
+ erts_print(to, to_arg, PTR_FMT ":l", htop);
+ dump_element(to, to_arg, htop[0]);
+ erts_putc(to, to_arg, '|');
+ dump_element(to, to_arg, htop[1]);
+ erts_putc(to, to_arg, '\n');
+ size = 2;
+ break;
+ }
+ htop += size;
+ }
+}
+
void erts_dump_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg)
{
char *s;
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 09aeba00fa..3dd3a60939 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -947,6 +947,8 @@ void erts_update_ranges(BeamInstr* code, Uint size);
void erts_remove_from_ranges(BeamInstr* code);
UWord erts_ranges_sz(void);
void erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info);
+ErtsLiteralArea** erts_dump_lit_areas;
+Uint erts_dump_num_lit_areas;
/* break.c */
void init_break_handler(void);
diff --git a/erts/emulator/beam/module.c b/erts/emulator/beam/module.c
index baeec115ea..1712dc803c 100644
--- a/erts/emulator/beam/module.c
+++ b/erts/emulator/beam/module.c
@@ -254,4 +254,3 @@ void module_end_staging(int commit)
IF_DEBUG(dbg_load_code_ix = -1);
}
-
diff --git a/erts/include/internal/erl_printf.h b/erts/include/internal/erl_printf.h
index f180a53f18..7e9807f6a8 100644
--- a/erts/include/internal/erl_printf.h
+++ b/erts/include/internal/erl_printf.h
@@ -44,6 +44,7 @@ struct erts_dsprintf_buf_t_ {
typedef int (*fmtfn_t)(void*, char*, size_t);
int erts_write_fd(void *vfdp, char* buf, size_t len);
+int erts_write_fp(void *vfdp, char* buf, size_t len);
int erts_write_ds(void *vdsbufp, char* buf, size_t len);
int erts_printf(const char *, ...);
diff --git a/erts/lib_src/common/erl_printf.c b/erts/lib_src/common/erl_printf.c
index 9031a4c5b7..1de0f81e84 100644
--- a/erts/lib_src/common/erl_printf.c
+++ b/erts/lib_src/common/erl_printf.c
@@ -143,8 +143,8 @@ write_f_add_cr(void *vfp, char* buf, size_t len)
return len;
}
-static int
-write_f(void *vfp, char* buf, size_t len)
+int
+erts_write_fp(void *vfp, char* buf, size_t len)
{
ASSERT(vfp);
#ifdef PUTC_ON_SMALL_WRITES
@@ -253,7 +253,7 @@ erts_printf(const char *format, ...)
FLOCKFILE(stdout);
res = erts_printf_format(erts_printf_add_cr_to_stdout
? write_f_add_cr
- : write_f,
+ : erts_write_fp,
(void *) stdout,
(char *) format,
arglist);
@@ -281,7 +281,7 @@ erts_fprintf(FILE *filep, const char *format, ...)
else if (erts_printf_add_cr_to_stderr && filep == stderr)
fmt_f = write_f_add_cr;
else
- fmt_f = write_f;
+ fmt_f = erts_write_fp;
FLOCKFILE(filep);
res = erts_printf_format(fmt_f,(void *)filep,(char *)format,arglist);
FUNLOCKFILE(filep);
@@ -386,7 +386,7 @@ erts_vprintf(const char *format, va_list arglist)
errno = 0;
res = erts_printf_format(erts_printf_add_cr_to_stdout
? write_f_add_cr
- : write_f,
+ : erts_write_fp,
(void *) stdout,
(char *) format,
arglist);
@@ -410,7 +410,7 @@ erts_vfprintf(FILE *filep, const char *format, va_list arglist)
else if (erts_printf_add_cr_to_stderr && filep == stderr)
fmt_f = write_f_add_cr;
else
- fmt_f = write_f;
+ fmt_f = erts_write_fp;
res = erts_printf_format(fmt_f,(void *)filep,(char *)format,arglist);
}
return res;
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index 51ff69fd69..7f6bb9bdcd 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -104,6 +104,7 @@
-define(index_table,index_table).
-define(instr_data,instr_data).
-define(internal_ets,internal_ets).
+-define(literals,literals).
-define(loaded_modules,loaded_modules).
-define(memory,memory).
-define(memory_map,memory_map).
@@ -785,6 +786,9 @@ parse_vsn_str(Str,WS) ->
%%% Progress is reported during the time and MUST be checked with
%%% crashdump_viewer:get_progress/0 until it returns {ok,done}.
do_read_file(File) ->
+ erase(?literals), %Clear literal cache.
+ put(truncated,false), %Not truncated (yet).
+ erase(truncated_reason), %Not truncated (yet).
case file:read_file_info(File) of
{ok,#file_info{type=regular,
access=FileA,
@@ -856,6 +860,19 @@ indexify(Fd,AddrAdj,Bin,N) ->
{?proc_heap,LastId} ->
[{_,LastPos}] = lookup_index(?proc_heap,LastId),
ets:insert(cdv_heap_file_chars,{LastId,N+Start+1-LastPos});
+ {?literals,[]} ->
+ case get(truncated_reason) of
+ undefined ->
+ [{_,LastPos}] = lookup_index(?literals,[]),
+ ets:insert(cdv_heap_file_chars,
+ {literals,N+Start+1-LastPos});
+ _ ->
+ %% Literals are truncated. Make sure we never
+ %% attempt to read in the literals. (Heaps that
+ %% references literals will show markers for
+ %% incomplete heaps, but will otherwise work.)
+ delete_index(?literals, [])
+ end;
_ -> ok
end,
indexify(Fd,AddrAdj,Rest,N1);
@@ -908,6 +925,7 @@ check_if_truncated() ->
find_truncated_proc({Tag,_Id}) when Tag==?atoms;
Tag==?binary;
Tag==?instr_data;
+ Tag==?literals;
Tag==?memory_status;
Tag==?memory_map ->
put(truncated_proc,false);
@@ -1386,13 +1404,33 @@ maybe_other_node2(Channel) ->
expand_memory(Fd,Pid,DumpVsn) ->
BinAddrAdj = get_bin_addr_adj(DumpVsn),
put(fd,Fd),
- Dict = read_heap(Fd,Pid,BinAddrAdj,gb_trees:empty()),
+ Dict0 = case get(?literals) of
+ undefined ->
+ Literals = read_literals(Fd),
+ put(?literals,Literals),
+ put(fd,Fd),
+ Literals;
+ Literals ->
+ Literals
+ end,
+ Dict = read_heap(Fd,Pid,BinAddrAdj,Dict0),
Expanded = {read_stack_dump(Fd,Pid,BinAddrAdj,Dict),
read_messages(Fd,Pid,BinAddrAdj,Dict),
read_dictionary(Fd,Pid,BinAddrAdj,Dict)},
erase(fd),
Expanded.
+read_literals(Fd) ->
+ case lookup_index(?literals,[]) of
+ [{_,Start}] ->
+ [{_,Chars}] = ets:lookup(cdv_heap_file_chars,literals),
+ init_progress("Reading literals",Chars),
+ pos_bof(Fd,Start),
+ read_heap(0,gb_trees:empty());
+ [] ->
+ gb_trees:empty()
+ end.
+
%%%-----------------------------------------------------------------
%%% This is a workaround for a bug in dump versions prior to 0.3:
%%% Addresses were truncated to 32 bits. This could cause binaries to
@@ -2591,8 +2629,26 @@ parse_heap_term("Ys"++Line0, Addr, BinAddrAdj, D0) -> %Sub binary.
end
end,
D = gb_trees:insert(Addr, Term, D0),
- {Term,Line,D}.
-
+ {Term,Line,D};
+parse_heap_term("Mf"++Line0, Addr, BinAddrAdj, D0) -> %Flatmap.
+ {Size,":"++Line1} = get_hex(Line0),
+ {Keys,":"++Line2,D1} = parse_term(Line1, BinAddrAdj, D0),
+ {Values,Line,D2} = parse_tuple(Size, Line2, Addr,BinAddrAdj, D1, []),
+ Pairs = zip_tuples(tuple_size(Keys), Keys, Values, []),
+ Map = maps:from_list(Pairs),
+ D = gb_trees:update(Addr, Map, D2),
+ {Map,Line,D};
+parse_heap_term("Mh"++Line0, Addr, BinAddrAdj, D0) -> %Head node in a hashmap.
+ {MapSize,":"++Line1} = get_hex(Line0),
+ {N,":"++Line2} = get_hex(Line1),
+ {Nodes,Line,D1} = parse_tuple(N, Line2, Addr, BinAddrAdj, D0, []),
+ Map = maps:from_list(flatten_hashmap_nodes(Nodes)),
+ MapSize = maps:size(Map), %Assertion.
+ D = gb_trees:update(Addr, Map, D1),
+ {Map,Line,D};
+parse_heap_term("Mn"++Line0, Addr, BinAddrAdj, D) -> %Interior node in a hashmap.
+ {N,":"++Line} = get_hex(Line0),
+ parse_tuple(N, Line, Addr, BinAddrAdj, D, []).
parse_tuple(0, Line, Addr, _, D0, Acc) ->
Tuple = list_to_tuple(lists:reverse(Acc)),
@@ -2606,6 +2662,25 @@ parse_tuple(N, Line0, Addr, BinAddrAdj, D0, Acc) ->
parse_tuple(N-1, Line, Addr, BinAddrAdj, D, [Term|Acc])
end.
+zip_tuples(0, _T1, _T2, Acc) ->
+ Acc;
+zip_tuples(N, T1, T2, Acc) when N =< tuple_size(T1) ->
+ zip_tuples(N-1, T1, T2, [{element(N, T1),element(N, T2)}|Acc]).
+
+flatten_hashmap_nodes(Tuple) ->
+ flatten_hashmap_nodes_1(tuple_size(Tuple), Tuple, []).
+
+flatten_hashmap_nodes_1(0, _Tuple, Acc) ->
+ Acc;
+flatten_hashmap_nodes_1(N, Tuple0, Acc0) ->
+ case element(N, Tuple0) of
+ [K|V] ->
+ flatten_hashmap_nodes_1(N-1, Tuple0, [{K,V}|Acc0]);
+ Tuple when is_tuple(Tuple) ->
+ Acc = flatten_hashmap_nodes_1(N-1, Tuple0, Acc0),
+ flatten_hashmap_nodes_1(tuple_size(Tuple), Tuple, Acc)
+ end.
+
parse_term([$H|Line0], BinAddrAdj, D) -> %Pointer to heap term.
{Ptr,Line} = get_hex(Line0),
deref_ptr(Ptr, Line, BinAddrAdj, D);
@@ -2794,6 +2869,10 @@ reset_tables() ->
insert_index(Tag,Id,Pos) ->
ets:insert(cdv_dump_index_table,{{Tag,Pos},Id}).
+delete_index(Tag,Id) ->
+ Ms = [{{{Tag,'$1'},Id},[],[true]}],
+ ets:select_delete(cdv_dump_index_table, Ms).
+
lookup_index({Tag,Id}) ->
lookup_index(Tag,Id);
lookup_index(Tag) ->
@@ -2810,6 +2889,7 @@ insert_binary_index(Addr,Pos) ->
lookup_binary_index(Addr) ->
ets:lookup(cdv_binary_index_table,Addr).
+
%%-----------------------------------------------------------------
%% Convert tags read from crashdump to atoms used as first part of key
%% in cdv_dump_index_table
@@ -2827,6 +2907,7 @@ tag_to_atom("hidden_node") -> ?hidden_node;
tag_to_atom("index_table") -> ?index_table;
tag_to_atom("instr_data") -> ?instr_data;
tag_to_atom("internal_ets") -> ?internal_ets;
+tag_to_atom("literals") -> ?literals;
tag_to_atom("loaded_modules") -> ?loaded_modules;
tag_to_atom("memory") -> ?memory;
tag_to_atom("mod") -> ?mod;
@@ -2850,8 +2931,10 @@ tag_to_atom(UnknownTag) ->
%%%-----------------------------------------------------------------
%%% Store last tag for use when truncated, and reason if aborted
put_last_tag(?abort,Reason) ->
- %% Don't overwrite the real last tag
- put(truncated_reason,Reason);
+ %% Don't overwrite the real last tag, and make sure to return
+ %% the previous last tag.
+ put(truncated_reason,Reason),
+ get(last_tag);
put_last_tag(Tag,Id) ->
put(last_tag,{Tag,Id}).
diff --git a/lib/observer/test/crashdump_helper.erl b/lib/observer/test/crashdump_helper.erl
index f37d9057cb..41041682c2 100644
--- a/lib/observer/test/crashdump_helper.erl
+++ b/lib/observer/test/crashdump_helper.erl
@@ -19,7 +19,9 @@
%%
-module(crashdump_helper).
--export([n1_proc/2,remote_proc/2]).
+-export([n1_proc/2,remote_proc/2,
+ dump_maps/0,create_maps/0,
+ create_binaries/0]).
-compile(r18).
-include_lib("common_test/include/ct.hrl").
@@ -60,6 +62,7 @@ n1_proc(Creator,_N2,Pid2,Port2,_L) ->
put(ref,Ref),
put(pid,Pid),
put(bin,Bin),
+ put(bins,create_binaries()),
put(sub_bin,SubBin),
put(bignum,83974938738373873),
put(neg_bignum,-38748762783736367),
@@ -92,3 +95,38 @@ remote_proc(P1,Creator) ->
Creator ! {self(),done},
receive after infinity -> ok end
end).
+
+create_binaries() ->
+ Sizes = lists:seq(60, 70) ++ lists:seq(120, 140),
+ [begin
+ <<H:16/unit:8>> = erlang:md5(<<Size:32>>),
+ Data = ((H bsl (8*150)) div (H+7919)),
+ <<Data:Size/unit:8>>
+ end || Size <- Sizes].
+
+%%%
+%%% Test dumping of maps. Dumping of maps only from OTP 20.2.
+%%%
+
+dump_maps() ->
+ Parent = self(),
+ F = fun() ->
+ register(aaaaaaaa_maps, self()),
+ put(maps, create_maps()),
+ Parent ! {self(),done},
+ receive _ -> ok end
+ end,
+ Pid = spawn_link(F),
+ receive
+ {Pid,done} ->
+ {ok,Pid}
+ end.
+
+create_maps() ->
+ Map0 = maps:from_list([{I,[I,I+1]} || I <- lists:seq(1, 40)]),
+ Map1 = maps:from_list([{I,{a,[I,I*I],{}}} || I <- lists:seq(1, 100)]),
+ Map2 = maps:from_list([{{I},(I*I) bsl 24} || I <- lists:seq(1, 10000)]),
+ Map3 = lists:foldl(fun(I, A) ->
+ A#{I=>I*I}
+ end, Map2, lists:seq(-10, 0)),
+ #{a=>Map0,b=>Map1,c=>Map2,d=>Map3,e=>#{}}.
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index f9ac884743..86a60e15f4 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -364,6 +364,10 @@ special(File,Procs) ->
crashdump_viewer:expand_binary({SOffset,SSize,SPos}),
io:format(" expand binary ok",[]),
+ Binaries = crashdump_helper:create_binaries(),
+ verify_binaries(Binaries, proplists:get_value(bins,Dict)),
+ io:format(" binaries ok",[]),
+
#proc{last_calls=LastCalls} = ProcDetails,
true = length(LastCalls) =< 4,
@@ -513,11 +517,36 @@ special(File,Procs) ->
io:format(" unicode table name ok",[]),
ok;
+ ".maps" ->
+ %% I registered a process as aaaaaaaa_maps in the map dump
+ %% to make sure it will be the first in the list when sorted
+ %% on names.
+ [#proc{pid=Pid0,name=Name}|_Rest] = lists:keysort(#proc.name,Procs),
+ "aaaaaaaa_maps" = Name,
+ Pid = pid_to_list(Pid0),
+ {ok,ProcDetails=#proc{},[]} = crashdump_viewer:proc_details(Pid),
+ io:format(" process details ok",[]),
+
+ #proc{dict=Dict} = ProcDetails,
+ io:format("~p\n", [Dict]),
+ Maps = crashdump_helper:create_maps(),
+ Maps = proplists:get_value(maps,Dict),
+ io:format(" maps ok",[]),
+ ok;
_ ->
ok
end,
ok.
+verify_binaries([H|T1], [H|T2]) ->
+ %% Heap binary.
+ verify_binaries(T1, T2);
+verify_binaries([Bin|T1], [['#CDVBin',Offset,Size,Pos]|T2]) ->
+ %% Refc binary.
+ {ok,<<Bin:Size/binary>>} = crashdump_viewer:expand_binary({Offset,Size,Pos}),
+ verify_binaries(T1, T2);
+verify_binaries([], []) ->
+ ok.
lookat_all_pids([]) ->
ok;
@@ -582,8 +611,9 @@ do_create_dumps(DataDir,Rel) ->
"-env ERL_CRASH_DUMP_BYTES " ++
integer_to_list(Bytes)),
CD6 = dump_with_unicode_atoms(DataDir,Rel,"unicode"),
+ CD7 = dump_with_maps(DataDir,Rel,"maps"),
TruncatedDumps = truncate_dump(CD1),
- {[CD1,CD2,CD3,CD4,CD5,CD6|TruncatedDumps], DosDump};
+ {[CD1,CD2,CD3,CD4,CD5,CD6,CD7|TruncatedDumps], DosDump};
_ ->
{[CD1,CD2], DosDump}
end.
@@ -596,7 +626,10 @@ truncate_dump(File) ->
{win32,_} -> <<"\r\n">>;
_ -> <<"\n">>
end,
- [StartBin,AfterTag] = binary:split(Bin,BinTag),
+ %% Split after "our binary" created by crashdump_helper
+ %% (it may not be the first binary).
+ RE = <<"\n=binary:(?=[0-9A-Z]+",NewLine/binary,"FF:010203)">>,
+ [StartBin,AfterTag] = re:split(Bin,RE,[{parts,2}]),
[AddrAndSize,BinaryAndRest] = binary:split(AfterTag,Colon),
[Binary,_Rest] = binary:split(BinaryAndRest,NewLine),
TruncSize = byte_size(Binary) - 2,
@@ -699,6 +732,16 @@ dump_with_unicode_atoms(DataDir,Rel,DumpName) ->
?t:stop_node(n1),
CD.
+dump_with_maps(DataDir,Rel,DumpName) ->
+ Opt = rel_opt(Rel),
+ Pz = "-pz \"" ++ filename:dirname(code:which(?MODULE)) ++ "\"",
+ PzOpt = [{args,Pz}],
+ {ok,N1} = ?t:start_node(n1,peer,Opt ++ PzOpt),
+ {ok,_Pid} = rpc:call(N1,crashdump_helper,dump_maps,[]),
+ CD = dump(N1,DataDir,Rel,DumpName),
+ ?t:stop_node(n1),
+ CD.
+
dump(Node,DataDir,Rel,DumpName) ->
Crashdump = filename:join(DataDir, dump_prefix(Rel)++DumpName),
rpc:call(Node,os,putenv,["ERL_CRASH_DUMP",Crashdump]),
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 786cd370f9..6788c1ee92 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -1323,9 +1323,9 @@ ec_normalize_params(#'ECParameters'{} = ECParams) ->
ec_normalize_params(Other) -> Other.
-spec ec_curve_spec(ecpk_parameters_api()) -> term().
-ec_curve_spec( #'ECParameters'{fieldID = FieldId, curve = PCurve, base = Base, order = Order, cofactor = CoFactor }) ->
- Field = {pubkey_cert_records:supportedCurvesTypes(FieldId#'FieldID'.fieldType),
- FieldId#'FieldID'.parameters},
+ec_curve_spec( #'ECParameters'{fieldID = #'FieldID'{fieldType = Type,
+ parameters = Params}, curve = PCurve, base = Base, order = Order, cofactor = CoFactor }) ->
+ Field = format_field(pubkey_cert_records:supportedCurvesTypes(Type), Params),
Curve = {PCurve#'Curve'.a, PCurve#'Curve'.b, none},
{Field, Curve, Base, Order, CoFactor};
ec_curve_spec({ecParameters, ECParams}) ->
@@ -1335,6 +1335,26 @@ ec_curve_spec({namedCurve, OID}) when is_tuple(OID), is_integer(element(1,OID))
ec_curve_spec({namedCurve, Name}) when is_atom(Name) ->
crypto:ec_curve(Name).
+format_field(characteristic_two_field = Type, Params0) ->
+ #'Characteristic-two'{
+ m = M,
+ basis = BasisOid,
+ parameters = Params} = der_decode('Characteristic-two', Params0),
+ {Type, M, field_param_decode(BasisOid, Params)};
+format_field(prime_field, Params0) ->
+ Prime = der_decode('Prime-p', Params0),
+ {prime_field, Prime}.
+
+field_param_decode(?'ppBasis', Params) ->
+ #'Pentanomial'{k1 = K1, k2 = K2, k3 = K3} =
+ der_decode('Pentanomial', Params),
+ {ppbasis, K1, K2, K3};
+field_param_decode(?'tpBasis', Params) ->
+ K = der_decode('Trinomial', Params),
+ {tpbasis, K};
+field_param_decode(?'gnBasis', _) ->
+ onbasis.
+
-spec ec_key({PubKey::term(), PrivateKey::term()}, Params::ecpk_parameters()) -> #'ECPrivateKey'{}.
ec_key({PubKey, PrivateKey}, Params) ->
#'ECPrivateKey'{version = 1,
diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index 0100f0a912..579df160bc 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.erl
@@ -64,7 +64,7 @@ all() ->
groups() ->
[{pem_decode_encode, [], [dsa_pem, rsa_pem, ec_pem, encrypted_pem,
dh_pem, cert_pem, pkcs7_pem, pkcs10_pem, ec_pem2,
- ec_pem_encode_generated]},
+ ec_pem_encode_generated, gen_ec_param]},
{ssh_public_key_decode_encode, [],
[ssh_rsa_public_key, ssh_dsa_public_key, ssh_ecdsa_public_key,
ssh_rfc4716_rsa_comment, ssh_rfc4716_dsa_comment,
@@ -97,7 +97,8 @@ end_per_group(_GroupName, Config) ->
Config.
%%-------------------------------------------------------------------
-init_per_testcase(pkix_test_data_all_default, Config) ->
+init_per_testcase(Case, Config) when Case == pkix_test_data_all_default;
+ Case == gen_ec_param ->
case crypto:ec_curves() of
[] ->
{skip, missing_ecc_support};
@@ -1206,7 +1207,13 @@ short_crl_issuer_hash(Config) when is_list(Config) ->
Issuer = public_key:pkix_crl_issuer(CrlDER),
CrlIssuerHash = public_key:short_name_hash(Issuer).
-
+%%--------------------------------------------------------------------
+gen_ec_param() ->
+ [{doc, "Generate key with EC parameters"}].
+gen_ec_param(Config) when is_list(Config) ->
+ Datadir = proplists:get_value(data_dir, Config),
+ do_gen_ec_param(filename:join(Datadir, "ec_key_param0.pem")),
+ do_gen_ec_param(filename:join(Datadir, "ec_key_param1.pem")).
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
@@ -1279,6 +1286,19 @@ strip_superfluous_newlines(Bin) ->
Str = string:strip(binary_to_list(Bin), right, 10),
re:replace(Str,"\n\n","\n", [{return,list}, global]).
+do_gen_ec_param(File) ->
+ {ok, KeyPem} = file:read_file(File),
+ Entries = public_key:pem_decode(KeyPem),
+ [ParamInfo] = [Entry || Entry={'EcpkParameters', _, not_encrypted} <- Entries],
+ {ecParameters, Params} = public_key:pem_entry_decode(ParamInfo),
+ Key = public_key:generate_key(Params),
+ case check_entry_type(Key, 'ECPrivateKey') of
+ true ->
+ ok;
+ false ->
+ ct:fail({key_gen_fail, File})
+ end.
+
incorrect_countryname_pkix_cert() ->
<<48,130,5,186,48,130,4,162,160,3,2,1,2,2,7,7,250,61,63,6,140,137,48,13,6,9,42, 134,72,134,247,13,1,1,5,5,0,48,129,220,49,11,48,9,6,3,85,4,6,19,2,85,83,49, 16,48,14,6,3,85,4,8,19,7,65,114,105,122,111,110,97,49,19,48,17,6,3,85,4,7,19, 10,83,99,111,116,116,115,100,97,108,101,49,37,48,35,6,3,85,4,10,19,28,83,116, 97,114,102,105,101,108,100,32,84,101,99,104,110,111,108,111,103,105,101,115, 44,32,73,110,99,46,49,57,48,55,6,3,85,4,11,19,48,104,116,116,112,58,47,47,99, 101,114,116,105,102,105,99,97,116,101,115,46,115,116,97,114,102,105,101,108, 100,116,101,99,104,46,99,111,109,47,114,101,112,111,115,105,116,111,114,121, 49,49,48,47,6,3,85,4,3,19,40,83,116,97,114,102,105,101,108,100,32,83,101,99, 117,114,101,32,67,101,114,116,105,102,105,99,97,116,105,111,110,32,65,117, 116,104,111,114,105,116,121,49,17,48,15,6,3,85,4,5,19,8,49,48,54,56,56,52,51, 53,48,30,23,13,49,48,49,48,50,51,48,49,51,50,48,53,90,23,13,49,50,49,48,50, 51,48,49,51,50,48,53,90,48,122,49,11,48,9,6,3,85,4,6,12,2,85,83,49,11,48,9,6, 3,85,4,8,12,2,65,90,49,19,48,17,6,3,85,4,7,12,10,83,99,111,116,116,115,100, 97,108,101,49,38,48,36,6,3,85,4,10,12,29,83,112,101,99,105,97,108,32,68,111, 109,97,105,110,32,83,101,114,118,105,99,101,115,44,32,73,110,99,46,49,33,48, 31,6,3,85,4,3,12,24,42,46,108,111,103,105,110,46,115,101,99,117,114,101,115, 101,114,118,101,114,46,110,101,116,48,130,1,34,48,13,6,9,42,134,72,134,247, 13,1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,185,136,240,80,141,36,124, 245,182,130,73,19,188,74,166,117,72,228,185,209,43,129,244,40,44,193,231,11, 209,12,234,88,43,142,1,162,48,122,17,95,230,105,171,131,12,147,46,204,36,80, 250,171,33,253,35,62,83,22,71,212,186,141,14,198,89,89,121,204,224,122,246, 127,110,188,229,162,67,95,6,74,231,127,99,131,7,240,85,102,203,251,50,58,58, 104,245,103,181,183,134,32,203,121,232,54,32,188,139,136,112,166,126,14,91, 223,153,172,164,14,61,38,163,208,215,186,210,136,213,143,70,147,173,109,217, 250,169,108,31,211,104,238,103,93,182,59,165,43,196,189,218,241,30,148,240, 109,90,69,176,194,52,116,173,151,135,239,10,209,179,129,192,102,75,11,25,168, 223,32,174,84,223,134,70,167,55,172,143,27,130,123,226,226,7,34,142,166,39, 48,246,96,231,150,84,220,106,133,193,55,95,159,227,24,249,64,36,1,142,171,16, 202,55,126,7,156,15,194,22,116,53,113,174,104,239,203,120,45,131,57,87,84, 163,184,27,83,57,199,91,200,34,43,98,61,180,144,76,65,170,177,2,3,1,0,1,163, 130,1,224,48,130,1,220,48,15,6,3,85,29,19,1,1,255,4,5,48,3,1,1,0,48,29,6,3, 85,29,37,4,22,48,20,6,8,43,6,1,5,5,7,3,1,6,8,43,6,1,5,5,7,3,2,48,14,6,3,85, 29,15,1,1,255,4,4,3,2,5,160,48,56,6,3,85,29,31,4,49,48,47,48,45,160,43,160, 41,134,39,104,116,116,112,58,47,47,99,114,108,46,115,116,97,114,102,105,101, 108,100,116,101,99,104,46,99,111,109,47,115,102,115,50,45,48,46,99,114,108, 48,83,6,3,85,29,32,4,76,48,74,48,72,6,11,96,134,72,1,134,253,110,1,7,23,2,48, 57,48,55,6,8,43,6,1,5,5,7,2,1,22,43,104,116,116,112,115,58,47,47,99,101,114, 116,115,46,115,116,97,114,102,105,101,108,100,116,101,99,104,46,99,111,109, 47,114,101,112,111,115,105,116,111,114,121,47,48,129,141,6,8,43,6,1,5,5,7,1, 1,4,129,128,48,126,48,42,6,8,43,6,1,5,5,7,48,1,134,30,104,116,116,112,58,47, 47,111,99,115,112,46,115,116,97,114,102,105,101,108,100,116,101,99,104,46,99, 111,109,47,48,80,6,8,43,6,1,5,5,7,48,2,134,68,104,116,116,112,58,47,47,99, 101,114,116,105,102,105,99,97,116,101,115,46,115,116,97,114,102,105,101,108, 100,116,101,99,104,46,99,111,109,47,114,101,112,111,115,105,116,111,114,121, 47,115,102,95,105,110,116,101,114,109,101,100,105,97,116,101,46,99,114,116, 48,31,6,3,85,29,35,4,24,48,22,128,20,73,75,82,39,209,27,188,242,161,33,106, 98,123,81,66,122,138,215,213,86,48,59,6,3,85,29,17,4,52,48,50,130,24,42,46, 108,111,103,105,110,46,115,101,99,117,114,101,115,101,114,118,101,114,46,110, 101,116,130,22,108,111,103,105,110,46,115,101,99,117,114,101,115,101,114,118, 101,114,46,110,101,116,48,29,6,3,85,29,14,4,22,4,20,138,233,191,208,157,203, 249,85,242,239,20,195,48,10,148,49,144,101,255,116,48,13,6,9,42,134,72,134, 247,13,1,1,5,5,0,3,130,1,1,0,82,31,121,162,49,50,143,26,167,202,143,61,71, 189,201,199,57,81,122,116,90,192,88,24,102,194,174,48,157,74,27,87,210,223, 253,93,3,91,150,109,120,1,110,27,11,200,198,141,222,246,14,200,71,105,41,138, 13,114,122,106,63,17,197,181,234,121,61,89,74,65,41,231,248,219,129,83,176, 219,55,107,55,211,112,98,38,49,69,77,96,221,108,123,152,12,210,159,157,141, 43,226,55,187,129,3,82,49,136,66,81,196,91,234,196,10,82,48,6,80,163,83,71, 127,102,177,93,209,129,26,104,2,84,24,255,248,161,3,244,169,234,92,122,110, 43,4,17,113,185,235,108,219,210,236,132,216,177,227,17,169,58,162,159,182, 162,93,160,229,200,9,163,229,110,121,240,168,232,14,91,214,188,196,109,210, 164,222,0,109,139,132,113,91,16,118,173,178,176,80,132,34,41,199,51,206,250, 224,132,60,115,192,94,107,163,219,212,226,225,65,169,148,108,213,46,174,173, 103,110,189,229,166,149,254,31,51,44,144,108,187,182,11,251,201,206,86,138, 208,59,51,86,132,235,81,225,88,34,190,8,184>>.
diff --git a/lib/public_key/test/public_key_SUITE_data/ec_key_param0.pem b/lib/public_key/test/public_key_SUITE_data/ec_key_param0.pem
new file mode 100644
index 0000000000..679b08f1a2
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ec_key_param0.pem
@@ -0,0 +1,28 @@
+-----BEGIN EC PARAMETERS-----
+MIIBwgIBATBNBgcqhkjOPQEBAkIB////////////////////////////////////
+//////////////////////////////////////////////////8wgZ4EQgH/////
+////////////////////////////////////////////////////////////////
+/////////////////ARBUZU+uWGOHJofkpohoLaFQO6i2nJbmbMV87i0iZGO8Qnh
+Vhk5Uex+k3sWUsC9O7G/BzVz34g9LDTx70Uf1GtQPwADFQDQnogAKRy4U5bMZxc5
+MoSqoNpkugSBhQQAxoWOBrcEBOnNnj7LZiOVtEKcZIE5BT+1Ifgor2BrTT26oUte
+d+/nWSj+HcEnov+o3jNIs8GFakKb+X5+McLlvWYBGDkpaniaO8AEXIpftCx9G9mY
+9URJV5tEaBevvRcnPmYsl+5ymV70JkDFULkBP60HYTU8cIaicsJAiL6Udp/RZlAC
+QgH///////////////////////////////////////////pRhoeDvy+Wa3/MAUj3
+CaXQO7XJuImcR667b7cekThkCQIBAQ==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIICnAIBAQRBP6XqBV/b2Q49a8RUvrJBwotzY+IErK5FkzgkExqJuyzXXMM3jMtd
+M1vlEF46OCjbldw7NaYITW1mpwz6z2xtqyagggHGMIIBwgIBATBNBgcqhkjOPQEB
+AkIB////////////////////////////////////////////////////////////
+//////////////////////////8wgZ4EQgH/////////////////////////////
+/////////////////////////////////////////////////////////ARBUZU+
+uWGOHJofkpohoLaFQO6i2nJbmbMV87i0iZGO8QnhVhk5Uex+k3sWUsC9O7G/BzVz
+34g9LDTx70Uf1GtQPwADFQDQnogAKRy4U5bMZxc5MoSqoNpkugSBhQQAxoWOBrcE
+BOnNnj7LZiOVtEKcZIE5BT+1Ifgor2BrTT26oUted+/nWSj+HcEnov+o3jNIs8GF
+akKb+X5+McLlvWYBGDkpaniaO8AEXIpftCx9G9mY9URJV5tEaBevvRcnPmYsl+5y
+mV70JkDFULkBP60HYTU8cIaicsJAiL6Udp/RZlACQgH/////////////////////
+//////////////////////pRhoeDvy+Wa3/MAUj3CaXQO7XJuImcR667b7cekThk
+CQIBAaGBiQOBhgAEAVtCjzs+HP67ZZheraLGJPY+iIJHwDYWeCyzn2J4/fOv5CTo
+x0+1QcjrECh4V4F6jfqwW/oQaG9KKyEWhvvWQ0yVAeBfE+89DhYB3h2kyelqRcwZ
+XUuB7n7TxijJiHJXq9b+u+sh0qH9ya6nbWjHk79V37pgQOegHjnRc1gIFz6IfRUa
+-----END EC PRIVATE KEY-----
diff --git a/lib/public_key/test/public_key_SUITE_data/ec_key_param1.pem b/lib/public_key/test/public_key_SUITE_data/ec_key_param1.pem
new file mode 100644
index 0000000000..67095d8dc5
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ec_key_param1.pem
@@ -0,0 +1,25 @@
+-----BEGIN EC PARAMETERS-----
+MIIBcQIBATAlBgcqhkjOPQECMBoCAgI7BgkqhkjOPQECAwMwCQIBAgIBBQIBCjBk
+BAEBBEgC9A5+IiHyld4pcRe389YvXGqX/8uM7/HNa6jOSpoYrYT/q72O+lkzK+et
+Z1ambilK/RhaeP8SqlIOTec5usoMf/7/fylVcnoDFQAqoFj3Og4zq0hrD2EEEMU6
+fxMjEASBkQQDAwAdNLhWKWwWwNQNPNd1CpPR0pVfqAql9A/I23sqvb3lOVD0wNKT
+zdcRo1tn+xSZrmADhhTxOUq/o7TIUNkn4ed2nI7sLRkDe/JzQtpjm23M//63PWnX
+jGwnpgCcu8oZgPhTOSHopoRCPkO6sIpXYpGvj0YbsqizUx0vBIXBmxbi8VFuI908
+GkgnrxuKwVsCSAP//////////////////////////////////////////////+Zh
+zhj/VZhzCAWbGGgjhR7H3ZyhFh3pPVF01m6Dgum7L+hORwIBAg==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIICXgIBAQRIAwaM3VblR1nZJseRVx4qJBQLLulB7uMF9KE+hMPlzZjj/f3KbcK4
+dfhxYFf4gqWNJW3fzFMEYF7JpzDjuFTmfX5jjO5f8cw9oIIBdTCCAXECAQEwJQYH
+KoZIzj0BAjAaAgICOwYJKoZIzj0BAgMDMAkCAQICAQUCAQowZAQBAQRIAvQOfiIh
+8pXeKXEXt/PWL1xql//LjO/xzWuozkqaGK2E/6u9jvpZMyvnrWdWpm4pSv0YWnj/
+EqpSDk3nObrKDH/+/38pVXJ6AxUAKqBY9zoOM6tIaw9hBBDFOn8TIxAEgZEEAwMA
+HTS4VilsFsDUDTzXdQqT0dKVX6gKpfQPyNt7Kr295TlQ9MDSk83XEaNbZ/sUma5g
+A4YU8TlKv6O0yFDZJ+HndpyO7C0ZA3vyc0LaY5ttzP/+tz1p14xsJ6YAnLvKGYD4
+Uzkh6KaEQj5DurCKV2KRr49GG7Kos1MdLwSFwZsW4vFRbiPdPBpIJ68bisFbAkgD
+///////////////////////////////////////////////mYc4Y/1WYcwgFmxho
+I4Uex92coRYd6T1RdNZug4Lpuy/oTkcCAQKhgZUDgZIABALOIIukF443IxnNZx1Q
+4HKDifQ9Lj3Rh+inIKczXLLJNEGCJ0wwG3d4v/fOxzdepZcnYviFAijQQA/iYWsA
+/Zet+5B4yxKISQcHei+PXkqwAAwEqq6D4hO2orlNOO430rgLoA0MNFc7I1THanOp
+q4RhYp8qnCEjM7nfQ4R0F+hPgZK1VPojkrGolaY/cNT/oA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl
index d644cbe66a..96782dcfc0 100644
--- a/lib/ssl/src/inet_tls_dist.erl
+++ b/lib/ssl/src/inet_tls_dist.erl
@@ -360,7 +360,7 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
Opts = trace(connect_options(get_ssl_options(client))),
dist_util:reset_timer(Timer),
case ssl:connect(
- Ip, TcpPort,
+ Address, TcpPort,
[binary, {active, false}, {packet, 4},
Driver:family(), nodelay()] ++ Opts,
net_kernel:connecttime()) of
diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl
index 919f8f20e6..a322bd002d 100644
--- a/lib/stdlib/src/filename.erl
+++ b/lib/stdlib/src/filename.erl
@@ -472,6 +472,10 @@ join(Name1, Name2) when is_atom(Name2) ->
join1([UcLetter, $:|Rest], RelativeName, [], win32)
when is_integer(UcLetter), UcLetter >= $A, UcLetter =< $Z ->
join1(Rest, RelativeName, [$:, UcLetter+$a-$A], win32);
+join1([$\\,$\\|Rest], RelativeName, [], win32) ->
+ join1([$/,$/|Rest], RelativeName, [], win32);
+join1([$/,$/|Rest], RelativeName, [], win32) ->
+ join1(Rest, RelativeName, [$/,$/], win32);
join1([$\\|Rest], RelativeName, Result, win32) ->
join1([$/|Rest], RelativeName, Result, win32);
join1([$/|Rest], RelativeName, [$., $/|Result], OsType) ->
@@ -500,6 +504,10 @@ join1([Atom|Rest], RelativeName, Result, OsType) when is_atom(Atom) ->
join1b(<<UcLetter, $:, Rest/binary>>, RelativeName, [], win32)
when is_integer(UcLetter), UcLetter >= $A, UcLetter =< $Z ->
join1b(Rest, RelativeName, [$:, UcLetter+$a-$A], win32);
+join1b(<<$\\,$\\,Rest/binary>>, RelativeName, [], win32) ->
+ join1b(<<$/,$/,Rest/binary>>, RelativeName, [], win32);
+join1b(<<$/,$/,Rest/binary>>, RelativeName, [], win32) ->
+ join1b(Rest, RelativeName, [$/,$/], win32);
join1b(<<$\\,Rest/binary>>, RelativeName, Result, win32) ->
join1b(<<$/,Rest/binary>>, RelativeName, Result, win32);
join1b(<<$/,Rest/binary>>, RelativeName, [$., $/|Result], OsType) ->
@@ -510,6 +518,8 @@ join1b(<<>>, <<>>, Result, OsType) ->
list_to_binary(maybe_remove_dirsep(Result, OsType));
join1b(<<>>, RelativeName, [$:|Rest], win32) ->
join1b(RelativeName, <<>>, [$:|Rest], win32);
+join1b(<<>>, RelativeName, [$/,$/|Result], win32) ->
+ join1b(RelativeName, <<>>, [$/,$/|Result], win32);
join1b(<<>>, RelativeName, [$/|Result], OsType) ->
join1b(RelativeName, <<>>, [$/|Result], OsType);
join1b(<<>>, RelativeName, [$., $/|Result], OsType) ->
@@ -523,6 +533,8 @@ maybe_remove_dirsep([$/, $:, Letter], win32) ->
[Letter, $:, $/];
maybe_remove_dirsep([$/], _) ->
[$/];
+maybe_remove_dirsep([$/,$/], win32) ->
+ [$/,$/];
maybe_remove_dirsep([$/|Name], _) ->
lists:reverse(Name);
maybe_remove_dirsep(Name, _) ->
@@ -712,6 +724,9 @@ win32_splitb(<<Letter0,$:,Rest/binary>>) when ?IS_DRIVELETTER(Letter0) ->
Letter = fix_driveletter(Letter0),
L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]),
[<<Letter,$:>> | [ X || X <- L, X =/= <<>> ]];
+win32_splitb(<<Slash,Slash,Rest/binary>>) when ((Slash =:= $\\) orelse (Slash =:= $/)) ->
+ L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]),
+ [<<"//">> | [ X || X <- L, X =/= <<>> ]];
win32_splitb(<<Slash,Rest/binary>>) when ((Slash =:= $\\) orelse (Slash =:= $/)) ->
L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]),
[<<$/>> | [ X || X <- L, X =/= <<>> ]];
@@ -723,6 +738,8 @@ win32_splitb(Name) ->
unix_split(Name) ->
split(Name, [], unix).
+win32_split([Slash,Slash|Rest]) when ((Slash =:= $\\) orelse (Slash =:= $/)) ->
+ split(Rest, [[$/,$/]], win32);
win32_split([$\\|Rest]) ->
win32_split([$/|Rest]);
win32_split([X, $\\|Rest]) when is_integer(X) ->
diff --git a/lib/stdlib/test/filename_SUITE.erl b/lib/stdlib/test/filename_SUITE.erl
index fc77593bb8..f284eb1ed6 100644
--- a/lib/stdlib/test/filename_SUITE.erl
+++ b/lib/stdlib/test/filename_SUITE.erl
@@ -107,6 +107,17 @@ absname(Config) when is_list(Config) ->
[Drive|":/erlang/src"] = filename:absname([Drive|":erlang/src"]),
"a:/erlang" = filename:absname("a:erlang"),
+ "//foo" = filename:absname("//foo"),
+ "//foo/bar" = filename:absname("//foo/bar"),
+ "//foo/\bar" = filename:absname("//foo/\bar"),
+ "//foo/bar/baz" = filename:absname("//foo/bar\\baz"),
+ "//foo/bar/baz" = filename:absname("//foo\\bar/baz"),
+ "//foo" = filename:absname("\\\\foo"),
+ "//foo/bar" = filename:absname("\\\\foo/bar"),
+ "//foo/\bar" = filename:absname("\\\\foo/\bar"),
+ "//foo/bar/baz" = filename:absname("\\\\foo/bar\\baz"),
+ "//foo/bar/baz" = filename:absname("\\\\foo\\bar/baz"),
+
file:set_cwd(Cwd),
ok;
{unix, _} ->
@@ -167,6 +178,23 @@ absname_2(Config) when is_list(Config) ->
[Drive|":/"]),
"a:/erlang" = filename:absname("a:erlang", [Drive|":/"]),
+ "//foo" = filename:absname("foo","//"),
+ "//foo/bar" = filename:absname("foo/bar", "//"),
+ "//foo/bar" = filename:absname("bar", "//foo"),
+ "//bar" = filename:absname("/bar", "//foo"),
+ "//foo/bar/baz" = filename:absname("bar/baz", "//foo"),
+ "//bar/baz" = filename:absname("//bar/baz", "//foo"),
+ "//\bar" = filename:absname("/\bar", "//foo"),
+ "//foo" = filename:absname("foo","\\\\"),
+ "//foo/bar" = filename:absname("foo/bar", "\\\\"),
+ "//foo/bar" = filename:absname("bar", "\\\\foo"),
+ "//bar" = filename:absname("/bar", "\\\\foo"),
+ "//foo/bar/baz" = filename:absname("bar/baz", "\\\\foo"),
+ "//bar/baz" = filename:absname("\\\\bar/baz", "\\\\foo"),
+ "//\bar" = filename:absname("/\bar", "\\\\foo"),
+ "//bar/baz" = filename:absname("\\\\bar/baz", "//foo"),
+ "//bar/baz" = filename:absname("//bar/baz", "\\\\foo"),
+
ok;
_ ->
"/usr/foo" = filename:absname(foo, "/usr"),
@@ -244,6 +272,18 @@ dirname(Config) when is_list(Config) ->
"A:usr" = filename:dirname("A:usr/foo.erl"),
"/usr" = filename:dirname("\\usr\\foo.erl"),
"/" = filename:dirname("\\usr"),
+ "//foo/bar" = filename:dirname("//foo/bar/baz.erl"),
+ "//foo/\bar" = filename:dirname("//foo/\bar/baz.erl"),
+ "//foo/bar" = filename:dirname("//foo\\bar/baz.erl"),
+ "//foo/bar" = filename:dirname("\\\\foo/bar/baz.erl"),
+ "//foo/\bar" = filename:dirname("\\\\foo/\bar/baz.erl"),
+ "//foo/bar" = filename:dirname("\\\\foo\\bar/baz.erl"),
+ "//foo" = filename:dirname("//foo/baz.erl"),
+ "//foo" = filename:dirname("//foo/\baz.erl"),
+ "//foo" = filename:dirname("//foo\\baz.erl"),
+ "//foo" = filename:dirname("\\\\foo/baz.erl"),
+ "//foo" = filename:dirname("\\\\foo/\baz.erl"),
+ "//foo" = filename:dirname("\\\\foo\\baz.erl"),
"A:" = filename:dirname("A:");
_ -> true
end,
@@ -289,7 +329,6 @@ join(Config) when is_list(Config) ->
%% join/1 and join/2 (OTP-12158) by using help function
%% filename_join/2.
"/" = filename:join(["/"]),
- "/" = filename:join(["//"]),
"usr/foo.erl" = filename_join("usr","foo.erl"),
"/src/foo.erl" = filename_join(usr, "/src/foo.erl"),
"/src/foo.erl" = filename_join("/src/",'foo.erl'),
@@ -301,7 +340,6 @@ join(Config) when is_list(Config) ->
"a/b/c/d/e/f/g" = filename_join("a//b/c/", "d//e/f/g"),
"a/b/c/d/e/f/g" = filename_join("a//b/c", "d//e/f/g"),
"/d/e/f/g" = filename_join("a//b/c", "/d//e/f/g"),
- "/d/e/f/g" = filename:join("a//b/c", "//d//e/f/g"),
"foo/bar" = filename_join([$f,$o,$o,$/,[]], "bar"),
@@ -332,6 +370,7 @@ join(Config) when is_list(Config) ->
case os:type() of
{win32, _} ->
+ "//" = filename:join(["//"]),
"d:/" = filename:join(["D:/"]),
"d:/" = filename:join(["D:\\"]),
"d:/abc" = filename_join("D:/", "abc"),
@@ -345,8 +384,35 @@ join(Config) when is_list(Config) ->
"c:/usr/foo.erl" = filename:join(["A:","C:/usr","foo.erl"]),
"c:usr/foo.erl" = filename:join(["A:","C:usr","foo.erl"]),
"d:/foo" = filename:join([$D, $:, $/, []], "foo"),
+ "//" = filename:join("\\\\", ""),
+ "//foo" = filename:join("\\\\", "foo"),
+ "//foo/bar" = filename:join("\\\\", "foo\\\\bar"),
+ "//foo/bar/baz" = filename:join("\\\\foo", "bar\\\\baz"),
+ "//foo/bar/baz" = filename:join("\\\\foo", "bar\\baz"),
+ "//foo/bar/baz" = filename:join("\\\\foo\\bar", baz),
+ "//foo/\bar/baz" = filename:join("\\\\foo/\bar", baz),
+ "//foo/bar/baz" = filename:join("\\\\foo/bar", baz),
+ "//bar/baz" = filename:join("\\\\foo", "\\\\bar\\baz"),
+ "//bar/baz" = filename:join("\\\\foo", "//bar\\baz"),
+ "//bar/baz" = filename:join("\\\\foo", "//bar/baz"),
+ "//bar/baz" = filename:join("\\\\foo", "\\\\bar/baz"),
+ "//d/e/f/g" = filename:join("a//b/c", "//d//e/f/g"),
+ "//" = filename:join("//", ""),
+ "//foo" = filename:join("//", "foo"),
+ "//foo/bar" = filename:join("//", "foo\\\\bar"),
+ "//foo/bar/baz" = filename:join("//foo", "bar\\\\baz"),
+ "//foo/bar/baz" = filename:join("//foo", "bar\\baz"),
+ "//foo/bar/baz" = filename:join("//foo\\bar", baz),
+ "//foo/\bar/baz" = filename:join("//foo/\bar", baz),
+ "//foo/bar/baz" = filename:join("//foo/bar", baz),
+ "//bar/baz" = filename:join("//foo", "\\\\bar\\baz"),
+ "//bar/baz" = filename:join("//foo", "//bar\\baz"),
+ "//bar/baz" = filename:join("//foo", "//bar/baz"),
+ "//bar/baz" = filename:join("//foo", "\\\\bar/baz"),
ok;
_ ->
+ "/" = filename:join(["//"]),
+ "/d/e/f/g" = filename:join("a//b/c", "//d//e/f/g"),
ok
end.
@@ -402,6 +468,16 @@ split(Config) when is_list(Config) ->
filename:split("a:\\msdev\\include"),
["a:","msdev","include"] =
filename:split("a:msdev\\include"),
+ ["//","foo"] =
+ filename:split("\\\\foo"),
+ ["//","foo"] =
+ filename:split("//foo"),
+ ["//","foo","bar"] =
+ filename:split("\\\\foo\\\\bar"),
+ ["//","foo","baz"] =
+ filename:split("\\\\foo\\baz"),
+ ["//","foo","baz"] =
+ filename:split("//foo\\baz"),
ok;
_ ->
ok
@@ -630,7 +706,6 @@ extension_bin(Config) when is_list(Config) ->
join_bin(Config) when is_list(Config) ->
<<"/">> = filename:join([<<"/">>]),
- <<"/">> = filename:join([<<"//">>]),
<<"usr/foo.erl">> = filename:join(<<"usr">>,<<"foo.erl">>),
<<"/src/foo.erl">> = filename:join(usr, <<"/src/foo.erl">>),
<<"/src/foo.erl">> = filename:join([<<"/src/">>,'foo.erl']),
@@ -642,7 +717,6 @@ join_bin(Config) when is_list(Config) ->
<<"a/b/c/d/e/f/g">> = filename:join([<<"a//b/c/">>, <<"d//e/f/g">>]),
<<"a/b/c/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"d//e/f/g">>]),
<<"/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"/d//e/f/g">>]),
- <<"/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"//d//e/f/g">>]),
<<"foo/bar">> = filename:join([$f,$o,$o,$/,[]], <<"bar">>),
@@ -695,6 +769,7 @@ join_bin(Config) when is_list(Config) ->
case os:type() of
{win32, _} ->
+ <<"//">> = filename:join([<<"//">>]),
<<"d:/">> = filename:join([<<"D:/">>]),
<<"d:/">> = filename:join([<<"D:\\">>]),
<<"d:/abc">> = filename:join([<<"D:/">>, <<"abc">>]),
@@ -708,8 +783,35 @@ join_bin(Config) when is_list(Config) ->
<<"c:/usr/foo.erl">> = filename:join([<<"A:">>,<<"C:/usr">>,<<"foo.erl">>]),
<<"c:usr/foo.erl">> = filename:join([<<"A:">>,<<"C:usr">>,<<"foo.erl">>]),
<<"d:/foo">> = filename:join([$D, $:, $/, []], <<"foo">>),
+ <<"//">> = filename:join(<<"\\\\">>, <<"">>),
+ <<"//foo">> = filename:join(<<"\\\\">>, <<"foo">>),
+ <<"//foo/bar">> = filename:join(<<"\\\\">>, <<"foo\\\\bar">>),
+ <<"//foo/bar/baz">> = filename:join(<<"\\\\foo">>, <<"bar\\\\baz">>),
+ <<"//bar/baz">> = filename:join(<<"\\\\foo">>, <<"\\\\bar\\baz">>),
+ <<"//foo/bar/baz">> = filename:join(<<"\\\\foo\\bar">>, baz),
+ <<"//foo/\bar/baz">> = filename:join(<<"\\\\foo/\bar">>, baz),
+ <<"//foo/bar/baz">> = filename:join(<<"\\\\foo/bar">>, baz),
+ <<"//bar/baz">> = filename:join(<<"\\\\foo">>, <<"\\\\bar\\baz">>),
+ <<"//bar/baz">> = filename:join(<<"\\\\foo">>, <<"//bar\\baz">>),
+ <<"//bar/baz">> = filename:join(<<"\\\\foo">>, <<"//bar/baz">>),
+ <<"//bar/baz">> = filename:join(<<"\\\\foo">>, <<"\\\\bar/baz">>),
+ <<"//d/e/f/g">> = filename:join([<<"a//b/c">>, <<"//d//e/f/g">>]),
+ <<"//">> = filename:join(<<"//">>, <<"">>),
+ <<"//foo">> = filename:join(<<"//">>, <<"foo">>),
+ <<"//foo/bar">> = filename:join(<<"//">>, <<"foo\\\\bar">>),
+ <<"//foo/bar/baz">> = filename:join(<<"//foo">>, <<"bar\\\\baz">>),
+ <<"//bar/baz">> = filename:join(<<"//foo">>, <<"\\\\bar\\baz">>),
+ <<"//foo/bar/baz">> = filename:join(<<"//foo\\bar">>, baz),
+ <<"//foo/\bar/baz">> = filename:join(<<"//foo/\bar">>, baz),
+ <<"//foo/bar/baz">> = filename:join(<<"//foo/bar">>, baz),
+ <<"//bar/baz">> = filename:join(<<"//foo">>, <<"\\\\bar\\baz">>),
+ <<"//bar/baz">> = filename:join(<<"//foo">>, <<"//bar\\baz">>),
+ <<"//bar/baz">> = filename:join(<<"//foo">>, <<"//bar/baz">>),
+ <<"//bar/baz">> = filename:join(<<"//foo">>, <<"\\\\bar/baz">>),
ok;
_ ->
+ <<"/">> = filename:join([<<"//">>]),
+ <<"/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"//d//e/f/g">>]),
ok
end.
@@ -756,6 +858,16 @@ split_bin(Config) when is_list(Config) ->
filename:split(<<"a:\\msdev\\include">>),
[<<"a:">>,<<"msdev">>,<<"include">>] =
filename:split(<<"a:msdev\\include">>),
+ [<<"//">>,<<"foo">>] =
+ filename:split(<<"\\\\foo">>),
+ [<<"//">>,<<"foo">>] =
+ filename:split(<<"//foo">>),
+ [<<"//">>,<<"foo">>,<<"bar">>] =
+ filename:split(<<"\\\\foo\\\\bar">>),
+ [<<"//">>,<<"foo">>,<<"baz">>] =
+ filename:split(<<"\\\\foo\\baz">>),
+ [<<"//">>,<<"foo">>,<<"baz">>] =
+ filename:split(<<"//foo\\baz">>),
ok;
_ ->
ok
diff --git a/lib/wx/c_src/wxe_driver.c b/lib/wx/c_src/wxe_driver.c
index 5da71818e5..26ae3564e7 100644
--- a/lib/wx/c_src/wxe_driver.c
+++ b/lib/wx/c_src/wxe_driver.c
@@ -33,6 +33,7 @@
#include <sys/types.h>
#include <fcntl.h>
+#include <assert.h>
#include "wxe_driver.h"
#define TEMP_BINARY_SIZE 512
@@ -234,13 +235,21 @@ standard_outputv(ErlDrvData drv_data, ErlIOVec* ev)
sd->max_bins = max;
}
- if(ev->vsize == 2) {
- binref->base = ev->iov[1].iov_base;
- binref->size = ev->iov[1].iov_len;
- binref->from = driver_caller(sd->port_handle);
- bin = ev->binv[1];
- driver_binary_inc_refc(bin); /* Otherwise it could get deallocated */
- binref->bin = bin;
+ if(ev->size > 0) {
+ assert(ev->vsize == 2 && ev->iov[0].iov_len == 0
+ && "erts changed how the ErlIOVec is structured for outputv");
+ binref->from = driver_caller(sd->port_handle);
+ binref->size = ev->iov[1].iov_len;
+ if(ev->binv[1]) {
+ binref->base = ev->iov[1].iov_base;
+ bin = ev->binv[1];
+ driver_binary_inc_refc(bin); /* Otherwise it could get deallocated */
+ } else {
+ bin = driver_alloc_binary(ev->iov[1].iov_len);
+ memcpy(bin->orig_bytes, ev->iov[1].iov_base, ev->iov[1].iov_len);
+ binref->base = bin->orig_bytes;
+ }
+ binref->bin = bin;
} else { /* Empty binary (becomes NULL) */
binref->base = NULL;
binref->size = 0;