aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/beam/erl_mtrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/beam/erl_mtrace.c')
-rw-r--r--erts/emulator/beam/erl_mtrace.c1240
1 files changed, 1240 insertions, 0 deletions
diff --git a/erts/emulator/beam/erl_mtrace.c b/erts/emulator/beam/erl_mtrace.c
new file mode 100644
index 0000000000..8b8ac2ec80
--- /dev/null
+++ b/erts/emulator/beam/erl_mtrace.c
@@ -0,0 +1,1240 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2003-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Description: Memory allocation trace. The trace is sent over a
+ * tcp/ip connection.
+ *
+ * The trace format is not intended to be documented.
+ * Instead a library for parsing the trace will be
+ * distributed. This in order to more easily be able
+ * to make changes in the trace format. The library
+ * for parsing the trace is currently not included in
+ * the OTP distribution, but will be in the future.
+ *
+ * Author: Rickard Green
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "global.h"
+#include "erl_sock.h"
+#include "erl_threads.h"
+#include "erl_memory_trace_protocol.h"
+#include "erl_mtrace.h"
+
+#if defined(MAXHOSTNAMELEN) && MAXHOSTNAMELEN > 255
+# undef MAXHOSTNAMELEN
+#endif
+
+#ifndef MAXHOSTNAMELEN
+# define MAXHOSTNAMELEN 255
+#endif
+
+#define TRACE_PRINTOUTS 0
+#ifdef TRACE_PRINTOUTS
+#define MSB2BITS(X) ((((unsigned)(X))+1)*8)
+#endif
+
+static erts_mtx_t mtrace_op_mutex;
+static erts_mtx_t mtrace_buf_mutex;
+
+#define TRACE_BUF_SZ (16*1024)
+
+
+#define UI8_MSB_EHF_SZ ERTS_MT_UI8_MSB_EHDR_FLD_SZ
+#define UI16_MSB_EHF_SZ ERTS_MT_UI16_MSB_EHDR_FLD_SZ
+#define UI32_MSB_EHF_SZ ERTS_MT_UI32_MSB_EHDR_FLD_SZ
+#define UI64_MSB_EHF_SZ ERTS_MT_UI64_MSB_EHDR_FLD_SZ
+#define UI_MSB_EHF_SZ ERTS_MT_UI64_MSB_EHDR_FLD_SZ
+#define TAG_EHF_SZ ERTS_MT_TAG_EHDR_FLD_SZ
+
+#define UI8_MSB_EHF_MSK ERTS_MT_UI8_MSB_EHDR_FLD_MSK
+#define UI16_MSB_EHF_MSK ERTS_MT_UI16_MSB_EHDR_FLD_MSK
+#define UI32_MSB_EHF_MSK ERTS_MT_UI32_MSB_EHDR_FLD_MSK
+#define UI_MSB_EHF_MSK ERTS_MT_UI64_MSB_EHDR_FLD_MSK
+#define UI64_MSB_EHF_MSK ERTS_MT_UI64_MSB_EHDR_FLD_MSK
+#define TAG_EHF_MSK ERTS_MT_TAG_EHDR_FLD_MSK
+
+#define UI8_SZ (1)
+#define UI16_SZ (2)
+#define UI32_SZ (4)
+#define UI64_SZ (8)
+#ifdef ARCH_64
+# define UI_SZ UI64_SZ
+#else
+# define UI_SZ UI32_SZ
+#endif
+
+#define WRITE_UI8(P, V) (*(P) = (byte) ((V) & 0xff))
+
+#define WRITE_UI16(P, V) \
+ ((P)[0] = (byte) (((V) >> 8) & 0xff), \
+ (P)[1] = (byte) ( (V) & 0xff))
+
+#define WRITE_UI32(P, V) \
+ ((P)[0] = (byte) (((V) >> 24) & 0xff), \
+ (P)[1] = (byte) (((V) >> 16) & 0xff), \
+ (P)[2] = (byte) (((V) >> 8) & 0xff), \
+ (P)[3] = (byte) ( (V) & 0xff))
+
+#define WRITE_UI64(P, V) \
+ ((P)[0] = (byte) (((V) >> 56) & 0xff), \
+ (P)[1] = (byte) (((V) >> 48) & 0xff), \
+ (P)[2] = (byte) (((V) >> 40) & 0xff), \
+ (P)[3] = (byte) (((V) >> 32) & 0xff), \
+ (P)[4] = (byte) (((V) >> 24) & 0xff), \
+ (P)[5] = (byte) (((V) >> 16) & 0xff), \
+ (P)[6] = (byte) (((V) >> 8) & 0xff), \
+ (P)[7] = (byte) ( (V) & 0xff))
+
+#define PUT_UI8(P, V) (WRITE_UI8((P), (V)), (P) += UI8_SZ)
+#define PUT_UI16(P, V) (WRITE_UI16((P), (V)), (P) += UI16_SZ)
+#define PUT_UI32(P, V) (WRITE_UI32((P), (V)), (P) += UI32_SZ)
+#define PUT_UI64(P, V) (WRITE_UI64((P), (V)), (P) += UI64_SZ)
+
+#define PUT_VSZ_UI16(P, M, V) \
+do { \
+ Uint16 v__ = (Uint16) (V); \
+ if (v__ >= (((Uint16) 1) << 8)) (M) = 1; else (M) = 0; \
+ switch ((M)) { \
+ case 1: *((P)++) = (byte) ((v__ >> 8) & 0xff); \
+ case 0: *((P)++) = (byte) ( v__ & 0xff); \
+ } \
+} while (0)
+
+#define PUT_VSZ_UI32(P, M, V) \
+do { \
+ Uint32 v__ = (Uint32) (V); \
+ if (v__ >= (((Uint32) 1) << 16)) { \
+ if (v__ >= (((Uint32) 1) << 24)) (M) = 3; else (M) = 2; \
+ } else { \
+ if (v__ >= (((Uint32) 1) << 8)) (M) = 1; else (M) = 0; \
+ } \
+ switch ((M)) { \
+ case 3: *((P)++) = (byte) ((v__ >> 24) & 0xff); \
+ case 2: *((P)++) = (byte) ((v__ >> 16) & 0xff); \
+ case 1: *((P)++) = (byte) ((v__ >> 8) & 0xff); \
+ case 0: *((P)++) = (byte) ( v__ & 0xff); \
+ } \
+} while (0)
+
+#ifdef ARCH_64
+
+#define PUT_VSZ_UI64(P, M, V) \
+do { \
+ Uint64 v__ = (Uint64) (V); \
+ if (v__ >= (((Uint64) 1) << 32)) { \
+ if (v__ >= (((Uint64) 1) << 48)) { \
+ if (v__ >= (((Uint64) 1) << 56)) (M) = 7; else (M) = 6; \
+ } else { \
+ if (v__ >= (((Uint64) 1) << 40)) (M) = 5; else (M) = 4; \
+ } \
+ } else { \
+ if (v__ >= (((Uint64) 1) << 16)) { \
+ if (v__ >= (((Uint64) 1) << 24)) (M) = 3; else (M) = 2; \
+ } else { \
+ if (v__ >= (((Uint64) 1) << 8)) (M) = 1; else (M) = 0; \
+ } \
+ } \
+ switch ((M)) { \
+ case 7: *((P)++) = (byte) ((v__ >> 56) & 0xff); \
+ case 6: *((P)++) = (byte) ((v__ >> 48) & 0xff); \
+ case 5: *((P)++) = (byte) ((v__ >> 40) & 0xff); \
+ case 4: *((P)++) = (byte) ((v__ >> 32) & 0xff); \
+ case 3: *((P)++) = (byte) ((v__ >> 24) & 0xff); \
+ case 2: *((P)++) = (byte) ((v__ >> 16) & 0xff); \
+ case 1: *((P)++) = (byte) ((v__ >> 8) & 0xff); \
+ case 0: *((P)++) = (byte) ( v__ & 0xff); \
+ } \
+} while (0)
+
+#define PUT_VSZ_UI PUT_VSZ_UI64
+#else /* #ifdef ARCH_64 */
+#define PUT_VSZ_UI PUT_VSZ_UI32
+#endif /* #ifdef ARCH_64 */
+
+#define MAKE_TBUF_SZ(SZ) \
+ (TRACE_BUF_SZ < (SZ) \
+ ? (disable_trace(1, "Internal buffer overflow", 0), 0) \
+ : (endp - tracep < (SZ) ? send_trace_buffer() : 1))
+
+
+static void disable_trace(int error, char *reason, int eno);
+static int send_trace_buffer(void);
+
+#ifdef DEBUG
+void
+check_alloc_entry(byte *sp, byte *ep,
+ byte tag,
+ Uint16 ct_no, int ct_no_n,
+ Uint16 type, int type_n,
+ Uint res, int res_n,
+ Uint size, int size_n,
+ Uint32 ti,int ti_n);
+void
+check_realloc_entry(byte *sp, byte *ep,
+ byte tag,
+ Uint16 ct_no, int ct_no_n,
+ Uint16 type, int type_n,
+ Uint res, int res_n,
+ Uint ptr, int ptr_n,
+ Uint size, int size_n,
+ Uint32 ti,int ti_n);
+void
+check_free_entry(byte *sp, byte *ep,
+ byte tag,
+ Uint16 ct_no, int ct_no_n,
+ Uint16 t_no, int t_no_n,
+ Uint ptr, int ptr_n,
+ Uint32 ti,int ti_n);
+void
+check_time_inc_entry(byte *sp, byte *ep,
+ Uint32 secs, int secs_n,
+ Uint32 usecs, int usecs_n);
+#endif
+
+
+
+int erts_mtrace_enabled;
+static erts_sock_t socket_desc;
+static byte trace_buffer[TRACE_BUF_SZ];
+static byte *tracep;
+static byte *endp;
+static SysTimeval last_tv;
+
+#if ERTS_MTRACE_SEGMENT_ID >= ERTS_ALC_A_MIN || ERTS_MTRACE_SEGMENT_ID < 0
+#error ERTS_MTRACE_SEGMENT_ID >= ERTS_ALC_A_MIN || ERTS_MTRACE_SEGMENT_ID < 0
+#endif
+
+char* erl_errno_id(int error);
+
+#define INVALID_TIME_INC (0xffffffff)
+
+static ERTS_INLINE Uint32
+get_time_inc(void)
+{
+ Sint32 secs;
+ Sint32 usecs;
+ Uint32 res;
+ SysTimeval tv;
+ sys_gettimeofday(&tv);
+
+ secs = tv.tv_sec - last_tv.tv_sec;
+ if (tv.tv_usec >= last_tv.tv_usec)
+ usecs = tv.tv_usec - last_tv.tv_usec;
+ else {
+ secs--;
+ usecs = 1000000 + tv.tv_usec - last_tv.tv_usec;
+ }
+
+ ASSERT(0 <= usecs);
+ ASSERT(usecs < 1000000);
+
+ if (secs < 0) {
+ /* Clock stepped backwards; we pretend that no time has past. */
+ res = 0;
+ }
+ else if (secs < ERTS_MT_TIME_INC_SECS_MASK) {
+ res = ((((Uint32) secs) << ERTS_MT_TIME_INC_SECS_SHIFT)
+ | (((Uint32) usecs) << ERTS_MT_TIME_INC_USECS_SHIFT));
+ }
+ else {
+ /* Increment too large to fit in a 32-bit integer;
+ put a time inc entry in trace ... */
+ if (MAKE_TBUF_SZ(UI8_SZ + UI16_SZ + 2*UI32_SZ)) {
+ byte *hdrp;
+ Uint16 hdr;
+ int secs_n, usecs_n;
+
+ *(tracep++) = ERTS_MT_TIME_INC_BDY_TAG;
+
+ hdrp = tracep;
+ tracep += 2;
+
+ PUT_VSZ_UI32(tracep, secs_n, secs);
+ PUT_VSZ_UI32(tracep, usecs_n, usecs);
+
+ hdr = usecs_n;
+
+ hdr <<= UI32_MSB_EHF_SZ;
+ hdr |= secs_n;
+
+ WRITE_UI16(hdrp, hdr);
+#ifdef DEBUG
+ check_time_inc_entry(hdrp-1, tracep,
+ (Uint32) secs, secs_n,
+ (Uint32) usecs, usecs_n);
+#endif
+ res = 0;
+ }
+ else {
+ res = INVALID_TIME_INC;
+ }
+ }
+
+ last_tv = tv;
+ return res;
+}
+
+
+static void
+disable_trace(int error, char *reason, int eno)
+{
+ char *mt_dis = "Memory trace disabled";
+ char *eno_str;
+
+ erts_mtrace_enabled = 0;
+ erts_sock_close(socket_desc);
+ socket_desc = ERTS_SOCK_INVALID_SOCKET;
+
+ if (eno == 0)
+ erts_fprintf(stderr, "%s: %s\n", mt_dis, reason);
+ else {
+ eno_str = erl_errno_id(eno);
+ if (strcmp(eno_str, "unknown") == 0)
+ erts_fprintf(stderr, "%s: %s: %d\n", mt_dis, reason, eno);
+ else
+ erts_fprintf(stderr, "%s: %s: %s\n", mt_dis, reason, eno_str);
+ }
+}
+
+static int
+send_trace_buffer(void)
+{
+ ssize_t ssz;
+ size_t sz;
+
+ sz = tracep - trace_buffer;
+ tracep = trace_buffer;
+
+ do {
+ ssz = erts_sock_send(socket_desc, (void *) tracep, sz);
+ if (ssz < 0) {
+ int socket_errno = erts_sock_errno();
+
+#ifdef EINTR
+ if (socket_errno == EINTR)
+ continue;
+#endif
+ disable_trace(0, "Connection lost", socket_errno);
+ return 0;
+ }
+ if (ssz > sz) {
+ disable_trace(1, "Unexpected error", 0);
+ return 0;
+ }
+ tracep += ssz;
+ sz -= ssz;
+ } while (sz);
+
+ tracep = trace_buffer;
+ return 1;
+}
+
+#if ERTS_ALC_N_MAX >= (1 << 16)
+#error "Excessively large type numbers"
+#endif
+
+
+static int
+write_trace_header(char *nodename, char *pid, char *hostname)
+{
+#ifdef DEBUG
+ byte *startp;
+#endif
+ Uint16 entry_sz;
+ Uint32 flags, n_len, h_len, p_len, hdr_prolog_len;
+ int i, no, str_len;
+ const char *str;
+ struct {
+ Uint32 gsec;
+ Uint32 sec;
+ Uint32 usec;
+ } start_time;
+
+ sys_gettimeofday(&last_tv);
+
+ start_time.gsec = (Uint32) (last_tv.tv_sec / 1000000000);
+ start_time.sec = (Uint32) (last_tv.tv_sec % 1000000000);
+ start_time.usec = (Uint32) last_tv.tv_usec;
+
+ if (!MAKE_TBUF_SZ(3*UI32_SZ))
+ return 0;
+
+ flags = 0;
+#ifdef ARCH_64
+ flags |= ERTS_MT_64_BIT_FLAG;
+#endif
+ flags |= ERTS_MT_CRR_INFO;
+#ifdef ERTS_CAN_TRACK_MALLOC
+ flags |= ERTS_MT_SEG_CRR_INFO;
+#endif
+
+ /*
+ * The following 3 ui32 words *always* have to come
+ * first in the trace.
+ */
+ PUT_UI32(tracep, ERTS_MT_START_WORD);
+ PUT_UI32(tracep, ERTS_MT_MAJOR_VSN);
+ PUT_UI32(tracep, ERTS_MT_MINOR_VSN);
+
+ n_len = strlen(nodename);
+ h_len = strlen(hostname);
+ p_len = strlen(pid);
+ hdr_prolog_len = (2*UI32_SZ
+ + 3*UI16_SZ
+ + 3*UI32_SZ
+ + 3*UI8_SZ
+ + n_len
+ + h_len
+ + p_len);
+
+ if (!MAKE_TBUF_SZ(hdr_prolog_len))
+ return 0;
+
+ /*
+ * New stuff can be added at the end the of header prolog
+ * (EOHP). The reader should skip stuff at the end, that it
+ * doesn't understand.
+ */
+
+#ifdef DEBUG
+ startp = tracep;
+#endif
+
+ PUT_UI32(tracep, hdr_prolog_len);
+ PUT_UI32(tracep, flags);
+ PUT_UI16(tracep, ERTS_MTRACE_SEGMENT_ID);
+ PUT_UI16(tracep, ERTS_ALC_A_MAX);
+ PUT_UI16(tracep, ERTS_ALC_N_MAX);
+
+ PUT_UI32(tracep, start_time.gsec);
+ PUT_UI32(tracep, start_time.sec);
+ PUT_UI32(tracep, start_time.usec);
+
+ PUT_UI8(tracep, (byte) n_len);
+ memcpy((void *) tracep, (void *) nodename, n_len);
+ tracep += n_len;
+
+ PUT_UI8(tracep, (byte) h_len);
+ memcpy((void *) tracep, (void *) hostname, h_len);
+ tracep += h_len;
+
+ PUT_UI8(tracep, (byte) p_len);
+ memcpy((void *) tracep, (void *) pid, p_len);
+ tracep += p_len;
+
+ ASSERT(startp + hdr_prolog_len == tracep);
+
+ /*
+ * EOHP
+ */
+
+ /*
+ * All tags from here on should be followed by an Uint16 size
+ * field containing the total size of the entry.
+ *
+ * New stuff can eigther be added at the end of an entry, or
+ * as a new tagged entry. The reader should skip stuff at the
+ * end, that it doesn't understand.
+ */
+
+ for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
+ Uint16 aflags = 0;
+
+#ifndef ERTS_CAN_TRACK_MALLOC
+ if (i != ERTS_ALC_A_SYSTEM)
+#endif
+ aflags |= ERTS_MT_ALLCTR_USD_CRR_INFO;
+
+ str = ERTS_ALC_A2AD(i);
+ ASSERT(str);
+ str_len = strlen(str);
+ if (str_len >= (1 << 8)) {
+ disable_trace(1, "Excessively large allocator string", 0);
+ return 0;
+ }
+
+ entry_sz = UI8_SZ + 3*UI16_SZ + UI8_SZ;
+ entry_sz += (erts_allctrs_info[i].alloc_util ? 2 : 1)*UI16_SZ;
+ entry_sz += UI8_SZ + str_len;
+
+ if (!MAKE_TBUF_SZ(entry_sz))
+ return 0;
+
+#ifdef DEBUG
+ startp = tracep;
+#endif
+ PUT_UI8(tracep, ERTS_MT_ALLOCATOR_HDR_TAG);
+ PUT_UI16(tracep, entry_sz);
+ PUT_UI16(tracep, aflags);
+ PUT_UI16(tracep, (Uint16) i);
+ PUT_UI8( tracep, (byte) str_len);
+ memcpy((void *) tracep, (void *) str, str_len);
+ tracep += str_len;
+ if (erts_allctrs_info[i].alloc_util) {
+ PUT_UI8(tracep, 2);
+ PUT_UI16(tracep, ERTS_MTRACE_SEGMENT_ID);
+ PUT_UI16(tracep, ERTS_ALC_A_SYSTEM);
+ }
+ else {
+ PUT_UI8(tracep, 1);
+ switch (i) {
+ case ERTS_ALC_A_SYSTEM:
+ PUT_UI16(tracep, ERTS_MTRACE_SEGMENT_ID);
+ break;
+ case ERTS_ALC_A_FIXED_SIZE:
+ if (erts_allctrs_info[ERTS_FIX_CORE_ALLOCATOR].enabled)
+ PUT_UI16(tracep, ERTS_FIX_CORE_ALLOCATOR);
+ else
+ PUT_UI16(tracep, ERTS_ALC_A_SYSTEM);
+ break;
+ default:
+ PUT_UI16(tracep, ERTS_MTRACE_SEGMENT_ID);
+ break;
+ }
+ }
+ ASSERT(startp + entry_sz == tracep);
+ }
+
+ for (i = ERTS_ALC_N_MIN; i <= ERTS_ALC_N_MAX; i++) {
+ Uint16 nflags = 0;
+ str = ERTS_ALC_N2TD(i);
+ ASSERT(str);
+
+ str_len = strlen(str);
+ if (str_len >= (1 << 8)) {
+ disable_trace(1, "Excessively large type string", 0);
+ return 0;
+ }
+
+ no = ERTS_ALC_T2A(ERTS_ALC_N2T(i));
+ if (!erts_allctrs_info[no].enabled)
+ no = ERTS_ALC_A_SYSTEM;
+ ASSERT(ERTS_ALC_A_MIN <= no && no <= ERTS_ALC_A_MAX);
+
+ entry_sz = UI8_SZ + 3*UI16_SZ + UI8_SZ + str_len + UI16_SZ;
+
+ if (!MAKE_TBUF_SZ(entry_sz))
+ return 0;
+
+#ifdef DEBUG
+ startp = tracep;
+#endif
+ PUT_UI8(tracep, ERTS_MT_BLOCK_TYPE_HDR_TAG);
+ PUT_UI16(tracep, entry_sz);
+ PUT_UI16(tracep, nflags);
+ PUT_UI16(tracep, (Uint16) i);
+ PUT_UI8(tracep, (byte) str_len);
+ memcpy((void *) tracep, (void *) str, str_len);
+ tracep += str_len;
+ PUT_UI16(tracep, no);
+ ASSERT(startp + entry_sz == tracep);
+ }
+
+ entry_sz = UI8_SZ + UI16_SZ;
+ if (!MAKE_TBUF_SZ(entry_sz))
+ return 0;
+ PUT_UI8(tracep, ERTS_MT_END_OF_HDR_TAG);
+ PUT_UI16(tracep, entry_sz);
+
+ return 1;
+}
+
+static void *mtrace_alloc(ErtsAlcType_t, void *, Uint);
+static void *mtrace_realloc(ErtsAlcType_t, void *, void *, Uint);
+static void mtrace_free(ErtsAlcType_t, void *, void *);
+
+static ErtsAllocatorFunctions_t real_allctrs[ERTS_ALC_A_MAX+1];
+
+void erts_mtrace_pre_init(void)
+{
+}
+
+void erts_mtrace_init(char *receiver, char *nodename)
+{
+ char hostname[MAXHOSTNAMELEN];
+ char pid[21]; /* enough for a 64 bit number */
+
+ socket_desc = ERTS_SOCK_INVALID_SOCKET;
+ erts_mtrace_enabled = receiver != NULL;
+
+ if (erts_mtrace_enabled) {
+ unsigned a, b, c, d, p;
+ byte ip_addr[4];
+ Uint16 port;
+
+ erts_mtx_init(&mtrace_buf_mutex, "mtrace_buf");
+ erts_mtx_set_forksafe(&mtrace_buf_mutex);
+ erts_mtx_init(&mtrace_op_mutex, "mtrace_op");
+ erts_mtx_set_forksafe(&mtrace_op_mutex);
+
+ socket_desc = erts_sock_open();
+ if (socket_desc == ERTS_SOCK_INVALID_SOCKET) {
+ disable_trace(1, "Failed to open socket", erts_sock_errno());
+ return;
+ }
+
+ if (5 != sscanf(receiver, "%u.%u.%u.%u:%u", &a, &b, &c, &d, &p)
+ || a >= (1 << 8) || b >= (1 << 8)|| c >= (1 << 8) || d >= (1 << 8)
+ || p >= (1 << 16)) {
+ disable_trace(1, "Invalid receiver address", 0);
+ return;
+ }
+
+ ip_addr[0] = (byte) a;
+ ip_addr[1] = (byte) b;
+ ip_addr[2] = (byte) c;
+ ip_addr[3] = (byte) d;
+
+ port = (Uint16) p;
+
+ if (!erts_sock_connect(socket_desc, ip_addr, 4, port)) {
+ disable_trace(1, "Failed to connect to receiver",
+ erts_sock_errno());
+ return;
+ }
+ tracep = trace_buffer;
+ endp = trace_buffer + TRACE_BUF_SZ;
+ if (erts_sock_gethostname(hostname, MAXHOSTNAMELEN) != 0)
+ hostname[0] = '\0';
+ hostname[MAXHOSTNAMELEN-1] = '\0';
+ sys_get_pid(pid);
+ write_trace_header(nodename ? nodename : "", pid, hostname);
+ erts_mtrace_update_heap_size();
+ }
+}
+
+void
+erts_mtrace_install_wrapper_functions(void)
+{
+ if (erts_mtrace_enabled) {
+ int i;
+ /* Install trace functions */
+ ASSERT(sizeof(erts_allctrs) == sizeof(real_allctrs));
+
+ sys_memcpy((void *) real_allctrs,
+ (void *) erts_allctrs,
+ sizeof(erts_allctrs));
+
+ for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
+ erts_allctrs[i].alloc = mtrace_alloc;
+ erts_allctrs[i].realloc = mtrace_realloc;
+ erts_allctrs[i].free = mtrace_free;
+ erts_allctrs[i].extra = (void *) &real_allctrs[i];
+ }
+ }
+}
+
+void
+erts_mtrace_stop(void)
+{
+ erts_mtx_lock(&mtrace_op_mutex);
+ erts_mtx_lock(&mtrace_buf_mutex);
+ if (erts_mtrace_enabled) {
+ Uint32 ti = get_time_inc();
+
+ if (ti != INVALID_TIME_INC
+ && MAKE_TBUF_SZ(UI8_SZ + UI16_SZ + UI32_SZ)) {
+ byte *hdrp;
+ Uint16 hdr;
+ int ti_n;
+
+ *(tracep++) = ERTS_MT_STOP_BDY_TAG;
+
+ hdrp = tracep;
+ tracep += 2;
+
+ PUT_VSZ_UI32(tracep, ti_n, ti);
+
+ hdr = ti_n;
+
+ WRITE_UI16(hdrp, hdr);
+
+ if(send_trace_buffer()) {
+ erts_mtrace_enabled = 0;
+ erts_sock_close(socket_desc);
+ socket_desc = ERTS_SOCK_INVALID_SOCKET;
+ }
+ }
+ }
+ erts_mtx_unlock(&mtrace_buf_mutex);
+ erts_mtx_unlock(&mtrace_op_mutex);
+}
+
+void
+erts_mtrace_exit(Uint32 exit_value)
+{
+ erts_mtx_lock(&mtrace_op_mutex);
+ erts_mtx_lock(&mtrace_buf_mutex);
+ if (erts_mtrace_enabled) {
+ Uint32 ti = get_time_inc();
+
+ if (ti != INVALID_TIME_INC
+ && MAKE_TBUF_SZ(UI8_SZ + UI16_SZ + 2*UI32_SZ)) {
+ byte *hdrp;
+ Uint16 hdr;
+ int ti_n, exit_value_n;
+
+ *(tracep++) = ERTS_MT_EXIT_BDY_TAG;
+
+ hdrp = tracep;
+ tracep += 2;
+
+ PUT_VSZ_UI32(tracep, exit_value_n, exit_value);
+ PUT_VSZ_UI32(tracep, ti_n, ti);
+
+ hdr = ti_n;
+
+ hdr <<= UI32_MSB_EHF_SZ;
+ hdr |= exit_value_n;
+
+ WRITE_UI16(hdrp, hdr);
+
+ if(send_trace_buffer()) {
+ erts_mtrace_enabled = 0;
+ erts_sock_close(socket_desc);
+ socket_desc = ERTS_SOCK_INVALID_SOCKET;
+ }
+ }
+ }
+ erts_mtx_unlock(&mtrace_buf_mutex);
+ erts_mtx_unlock(&mtrace_op_mutex);
+}
+
+static ERTS_INLINE void
+write_alloc_entry(byte tag,
+ void *res,
+ ErtsAlcType_t x,
+ ErtsAlcType_t y,
+ Uint size)
+{
+ erts_mtx_lock(&mtrace_buf_mutex);
+ if (erts_mtrace_enabled) {
+ Uint32 ti = get_time_inc();
+
+ if (ti != INVALID_TIME_INC
+ && MAKE_TBUF_SZ(UI8_SZ + 2*UI16_SZ + 2*UI_SZ + UI32_SZ)) {
+ Uint16 hdr, t_no = (Uint16) x, ct_no = (Uint16) y;
+ byte *hdrp;
+ int t_no_n, ct_no_n = 0, res_n, size_n, ti_n;
+
+ *(tracep++) = tag;
+
+ hdrp = tracep;
+ tracep += 2;
+
+ if (tag == ERTS_MT_CRR_ALLOC_BDY_TAG) {
+ PUT_VSZ_UI16(tracep, ct_no_n, ct_no);
+ }
+ PUT_VSZ_UI16(tracep, t_no_n, t_no);
+ PUT_VSZ_UI( tracep, res_n, res);
+ PUT_VSZ_UI( tracep, size_n, size);
+ PUT_VSZ_UI32(tracep, ti_n, ti);
+
+ hdr = ti_n;
+
+ hdr <<= UI_MSB_EHF_SZ;
+ hdr |= size_n;
+
+ hdr <<= UI_MSB_EHF_SZ;
+ hdr |= res_n;
+
+ hdr <<= UI16_MSB_EHF_SZ;
+ hdr |= t_no_n;
+
+ if (tag == ERTS_MT_CRR_ALLOC_BDY_TAG) {
+ hdr <<= UI16_MSB_EHF_SZ;
+ hdr |= ct_no_n;
+ }
+
+ WRITE_UI16(hdrp, hdr);
+
+#if TRACE_PRINTOUTS
+ print_trace_entry(tag,
+ ct_no, ct_no_n,
+ t_no, t_no_n,
+ (Uint) res, res_n,
+ 0, 0,
+ size, size_n,
+ ti, ti_n);
+#endif
+
+#ifdef DEBUG
+ check_alloc_entry(hdrp-1, tracep,
+ tag,
+ ct_no, ct_no_n,
+ t_no, t_no_n,
+ (Uint) res, res_n,
+ size, size_n,
+ ti, ti_n);
+#endif
+
+ }
+
+ }
+ erts_mtx_unlock(&mtrace_buf_mutex);
+
+}
+
+static ERTS_INLINE void
+write_realloc_entry(byte tag,
+ void *res,
+ ErtsAlcType_t x,
+ ErtsAlcType_t y,
+ void *ptr,
+ Uint size)
+{
+ erts_mtx_lock(&mtrace_buf_mutex);
+ if (erts_mtrace_enabled) {
+ Uint32 ti = get_time_inc();
+
+ if (ti != INVALID_TIME_INC
+ && MAKE_TBUF_SZ(UI8_SZ + 2*UI16_SZ + 3*UI_SZ + UI32_SZ)) {
+ Uint16 hdr, t_no = (Uint16) x, ct_no = (Uint16) y;
+ byte *hdrp;
+ int t_no_n, ct_no_n = 0, res_n, ptr_n, size_n, ti_n;
+
+ *(tracep++) = tag;
+
+ hdrp = tracep;
+ tracep += 2;
+
+ if (tag == ERTS_MT_CRR_REALLOC_BDY_TAG) {
+ PUT_VSZ_UI16(tracep, ct_no_n, ct_no);
+ }
+ PUT_VSZ_UI16(tracep, t_no_n, t_no);
+ PUT_VSZ_UI( tracep, res_n, res);
+ PUT_VSZ_UI( tracep, ptr_n, ptr);
+ PUT_VSZ_UI( tracep, size_n, size);
+ PUT_VSZ_UI32(tracep, ti_n, ti);
+
+ hdr = ti_n;
+
+ hdr <<= UI_MSB_EHF_SZ;
+ hdr |= size_n;
+
+ hdr <<= UI_MSB_EHF_SZ;
+ hdr |= ptr_n;
+
+ hdr <<= UI_MSB_EHF_SZ;
+ hdr |= res_n;
+
+ hdr <<= UI16_MSB_EHF_SZ;
+ hdr |= t_no_n;
+
+ if (tag == ERTS_MT_CRR_REALLOC_BDY_TAG) {
+ hdr <<= UI16_MSB_EHF_SZ;
+ hdr |= ct_no_n;
+ }
+
+ WRITE_UI16(hdrp, hdr);
+
+#if TRACE_PRINTOUTS
+ print_trace_entry(tag,
+ ct_no, ct_no_n,
+ t_no, t_no_n,
+ (Uint) res, res_n,
+ (Uint) ptr, ptr_n,
+ size, size_n,
+ ti, ti_n);
+#endif
+
+#ifdef DEBUG
+ check_realloc_entry(hdrp-1, tracep,
+ tag,
+ ct_no, ct_no_n,
+ t_no, t_no_n,
+ (Uint) res, res_n,
+ (Uint) ptr, ptr_n,
+ size, size_n,
+ ti, ti_n);
+#endif
+
+ }
+ }
+ erts_mtx_unlock(&mtrace_buf_mutex);
+}
+
+static ERTS_INLINE void
+write_free_entry(byte tag,
+ ErtsAlcType_t x,
+ ErtsAlcType_t y,
+ void *ptr)
+{
+ erts_mtx_lock(&mtrace_buf_mutex);
+ if (erts_mtrace_enabled) {
+ Uint32 ti = get_time_inc();
+
+ if (ti != INVALID_TIME_INC
+ && MAKE_TBUF_SZ(UI8_SZ + 2*UI16_SZ + UI_SZ + UI32_SZ)) {
+ Uint16 hdr, t_no = (Uint16) x, ct_no = (Uint16) y;
+ byte *hdrp;
+ int t_no_n, ct_no_n = 0, ptr_n, ti_n;
+
+ *(tracep++) = tag;
+
+ hdrp = tracep;
+ tracep += 2;
+
+ if (tag == ERTS_MT_CRR_FREE_BDY_TAG) {
+ PUT_VSZ_UI16(tracep, ct_no_n, ct_no);
+ }
+ PUT_VSZ_UI16(tracep, t_no_n, t_no);
+ PUT_VSZ_UI( tracep, ptr_n, ptr);
+ PUT_VSZ_UI32(tracep, ti_n, ti);
+
+ hdr = ti_n;
+
+ hdr <<= UI_MSB_EHF_SZ;
+ hdr |= ptr_n;
+
+ hdr <<= UI16_MSB_EHF_SZ;
+ hdr |= t_no_n;
+
+ if (tag == ERTS_MT_CRR_FREE_BDY_TAG) {
+ hdr <<= UI16_MSB_EHF_SZ;
+ hdr |= ct_no_n;
+ }
+
+ WRITE_UI16(hdrp, hdr);
+
+#if TRACE_PRINTOUTS
+ print_trace_entry(tag,
+ ct_no, ct_no_n,
+ t_no, t_no_n,
+ (Uint) 0, 0,
+ (Uint) ptr, ptr_n,
+ 0, 0,
+ ti, ti_n);
+#endif
+
+#ifdef DEBUG
+ check_free_entry(hdrp-1, tracep,
+ tag,
+ ct_no, ct_no_n,
+ t_no, t_no_n,
+ (Uint) ptr, ptr_n,
+ ti, ti_n);
+#endif
+ }
+
+ }
+ erts_mtx_unlock(&mtrace_buf_mutex);
+}
+
+static void *
+mtrace_alloc(ErtsAlcType_t n, void *extra, Uint size)
+{
+ ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra;
+ void *res;
+
+ erts_mtx_lock(&mtrace_op_mutex);
+
+ res = (*real_af->alloc)(n, real_af->extra, size);
+ write_alloc_entry(ERTS_MT_ALLOC_BDY_TAG, res, n, 0, size);
+
+ erts_mtx_unlock(&mtrace_op_mutex);
+
+ return res;
+}
+
+static void *
+mtrace_realloc(ErtsAlcType_t n, void *extra, void *ptr, Uint size)
+{
+ ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra;
+ void *res;
+
+ erts_mtx_lock(&mtrace_op_mutex);
+
+ res = (*real_af->realloc)(n, real_af->extra, ptr, size);
+ write_realloc_entry(ERTS_MT_REALLOC_BDY_TAG, res, n, 0, ptr, size);
+
+ erts_mtx_unlock(&mtrace_op_mutex);
+
+ return res;
+
+}
+
+static void
+mtrace_free(ErtsAlcType_t n, void *extra, void *ptr)
+{
+ ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra;
+
+ erts_mtx_lock(&mtrace_op_mutex);
+
+ (*real_af->free)(n, real_af->extra, ptr);
+ write_free_entry(ERTS_MT_FREE_BDY_TAG, n, 0, ptr);
+
+ erts_mtx_unlock(&mtrace_op_mutex);
+}
+
+
+void
+erts_mtrace_crr_alloc(void *res, ErtsAlcType_t n, ErtsAlcType_t m, Uint size)
+{
+ write_alloc_entry(ERTS_MT_CRR_ALLOC_BDY_TAG, res, n, m, size);
+}
+
+void
+erts_mtrace_crr_realloc(void *res, ErtsAlcType_t n, ErtsAlcType_t m, void *ptr,
+ Uint size)
+{
+ write_realloc_entry(ERTS_MT_CRR_REALLOC_BDY_TAG, res, n, m, ptr, size);
+}
+
+void
+erts_mtrace_crr_free(ErtsAlcType_t n, ErtsAlcType_t m, void *ptr)
+{
+ write_free_entry(ERTS_MT_CRR_FREE_BDY_TAG, n, m, ptr);
+}
+
+
+#if TRACE_PRINTOUTS
+static void
+print_trace_entry(byte tag,
+ Uint16 t_no, int t_no_n,
+ Uint16 ct_no, int ct_no_n,
+ Uint res, int res_n,
+ Uint ptr, int ptr_n,
+ Uint size, int size_n,
+ Uint32 ti,int ti_n)
+{
+ switch (tag) {
+ case ERTS_MT_ALLOC_BDY_TAG:
+ fprintf(stderr,
+ "{alloc, {%lu, %lu, %lu}, {%u, %u, %u, %u}}\n\r",
+
+ (unsigned long) t_no, (unsigned long) res,
+ (unsigned long) size,
+
+ MSB2BITS(t_no_n), MSB2BITS(res_n),
+ MSB2BITS(size_n), MSB2BITS(ti_n));
+ break;
+ case ERTS_MT_REALLOC_BDY_TAG:
+ fprintf(stderr,
+ "{realloc, {%lu, %lu, %lu, %lu}, {%u, %u, %u, %u, %u}}\n\r",
+
+ (unsigned long) t_no, (unsigned long) res,
+ (unsigned long) ptr, (unsigned long) size,
+
+ MSB2BITS(t_no_n), MSB2BITS(res_n),
+ MSB2BITS(ptr_n), MSB2BITS(size_n), MSB2BITS(ti_n));
+ break;
+ case ERTS_MT_FREE_BDY_TAG:
+ fprintf(stderr,
+ "{free, {%lu, %lu}, {%u, %u, %u, %u, %u}}\n\r",
+
+ (unsigned long) t_no, (unsigned long) ptr,
+
+ MSB2BITS(t_no_n), MSB2BITS(ptr_n), MSB2BITS(ti_n));
+ break;
+ case ERTS_MT_CRR_ALLOC_BDY_TAG:
+ fprintf(stderr,
+ "{crr_alloc, {%lu, %lu, %lu, %lu}, {%u, %u, %u, %u, %u}}\n\r",
+
+ (unsigned long) ct_no, (unsigned long) t_no,
+ (unsigned long) res, (unsigned long) size,
+
+ MSB2BITS(ct_no_n), MSB2BITS(t_no_n),
+ MSB2BITS(res_n), MSB2BITS(size_n),
+ MSB2BITS(ti_n));
+ break;
+ case ERTS_MT_CRR_REALLOC_BDY_TAG:
+ fprintf(stderr,
+ "{crr_realloc, {%lu, %lu, %lu, %lu, %lu}, "
+ "{%u, %u, %u, %u, %u, %u}}\n\r",
+
+ (unsigned long) ct_no, (unsigned long) t_no,
+ (unsigned long) res, (unsigned long) ptr,
+ (unsigned long) size,
+
+ MSB2BITS(ct_no_n), MSB2BITS(t_no_n),
+ MSB2BITS(res_n), MSB2BITS(ptr_n),
+ MSB2BITS(size_n), MSB2BITS(ti_n));
+ break;
+ case ERTS_MT_CRR_FREE_BDY_TAG:
+ fprintf(stderr,
+ "{crr_free, {%lu, %lu, %lu}, {%u, %u, %u, %u}}\n\r",
+
+ (unsigned long) ct_no, (unsigned long) t_no,
+ (unsigned long) ptr,
+
+ MSB2BITS(ct_no_n), MSB2BITS(t_no_n),
+ MSB2BITS(ptr_n), MSB2BITS(ti_n));
+ break;
+ default:
+ fprintf(stderr, "{'\?\?\?'}\n\r");
+ break;
+ }
+}
+
+#endif /* #if TRACE_PRINTOUTS */
+
+#ifdef DEBUG
+
+#define GET_UI16(P) ((P) += UI16_SZ, \
+ (((Uint16) (*((P) - 2) << 8)) | ((Uint16) (*((P) - 1)))))
+
+static void
+check_ui(Uint16 *hdrp, byte **pp, Uint ui, int msb,
+ Uint16 f_mask, Uint16 f_size)
+{
+ Uint x;
+ int n;
+
+ ASSERT((msb & ~f_mask) == 0);
+
+ n = (int) (*hdrp & f_mask);
+
+ ASSERT(n == msb);
+
+ *hdrp >>= f_size;
+
+ x = 0;
+ switch (n) {
+#ifdef ARCH_64
+ case 7: x |= *((*pp)++); x <<= 8;
+ case 6: x |= *((*pp)++); x <<= 8;
+ case 5: x |= *((*pp)++); x <<= 8;
+ case 4: x |= *((*pp)++); x <<= 8;
+#endif
+ case 3: x |= *((*pp)++); x <<= 8;
+ case 2: x |= *((*pp)++); x <<= 8;
+ case 1: x |= *((*pp)++); x <<= 8;
+ case 0: x |= *((*pp)++); break;
+ default: ASSERT(0);
+ }
+
+ ASSERT(x == ui);
+}
+
+
+void
+check_alloc_entry(byte *sp, byte *ep,
+ byte tag,
+ Uint16 ct_no, int ct_no_n,
+ Uint16 t_no, int t_no_n,
+ Uint res, int res_n,
+ Uint size, int size_n,
+ Uint32 ti,int ti_n)
+{
+ byte *p = sp;
+ Uint16 hdr;
+
+ ASSERT(*p == tag);
+ p++;
+
+ hdr = GET_UI16(p);
+
+ if (tag == ERTS_MT_CRR_ALLOC_BDY_TAG)
+ check_ui(&hdr, &p, ct_no, ct_no_n, UI16_MSB_EHF_MSK, UI16_MSB_EHF_SZ);
+ check_ui(&hdr, &p, t_no, t_no_n, UI16_MSB_EHF_MSK, UI16_MSB_EHF_SZ);
+ check_ui(&hdr, &p, res, res_n, UI_MSB_EHF_MSK, UI_MSB_EHF_SZ);
+ check_ui(&hdr, &p, size, size_n, UI_MSB_EHF_MSK, UI_MSB_EHF_SZ);
+ check_ui(&hdr, &p, ti, ti_n, UI32_MSB_EHF_MSK, UI32_MSB_EHF_SZ);
+
+ ASSERT(hdr == 0);
+ ASSERT(p == ep);
+}
+
+void
+check_realloc_entry(byte *sp, byte *ep,
+ byte tag,
+ Uint16 ct_no, int ct_no_n,
+ Uint16 t_no, int t_no_n,
+ Uint res, int res_n,
+ Uint ptr, int ptr_n,
+ Uint size, int size_n,
+ Uint32 ti,int ti_n)
+{
+ byte *p = sp;
+ Uint16 hdr;
+
+ ASSERT(*p == tag);
+ p++;
+
+ hdr = GET_UI16(p);
+
+ if (tag == ERTS_MT_CRR_REALLOC_BDY_TAG)
+ check_ui(&hdr, &p, ct_no, ct_no_n, UI16_MSB_EHF_MSK, UI16_MSB_EHF_SZ);
+ check_ui(&hdr, &p, t_no, t_no_n, UI16_MSB_EHF_MSK, UI16_MSB_EHF_SZ);
+ check_ui(&hdr, &p, res, res_n, UI_MSB_EHF_MSK, UI_MSB_EHF_SZ);
+ check_ui(&hdr, &p, ptr, ptr_n, UI_MSB_EHF_MSK, UI_MSB_EHF_SZ);
+ check_ui(&hdr, &p, size, size_n, UI_MSB_EHF_MSK, UI_MSB_EHF_SZ);
+ check_ui(&hdr, &p, ti, ti_n, UI32_MSB_EHF_MSK, UI32_MSB_EHF_SZ);
+
+ ASSERT(hdr == 0);
+ ASSERT(p == ep);
+}
+
+void
+check_free_entry(byte *sp, byte *ep,
+ byte tag,
+ Uint16 ct_no, int ct_no_n,
+ Uint16 t_no, int t_no_n,
+ Uint ptr, int ptr_n,
+ Uint32 ti,int ti_n)
+{
+ byte *p = sp;
+ Uint16 hdr;
+
+ ASSERT(*p == tag);
+ p++;
+
+ hdr = GET_UI16(p);
+
+ if (tag == ERTS_MT_CRR_FREE_BDY_TAG)
+ check_ui(&hdr, &p, ct_no, ct_no_n, UI16_MSB_EHF_MSK, UI16_MSB_EHF_SZ);
+ check_ui(&hdr, &p, t_no, t_no_n, UI16_MSB_EHF_MSK, UI16_MSB_EHF_SZ);
+ check_ui(&hdr, &p, ptr, ptr_n, UI_MSB_EHF_MSK, UI_MSB_EHF_SZ);
+ check_ui(&hdr, &p, ti, ti_n, UI32_MSB_EHF_MSK, UI32_MSB_EHF_SZ);
+
+ ASSERT(hdr == 0);
+ ASSERT(p == ep);
+
+}
+
+void
+check_time_inc_entry(byte *sp, byte *ep,
+ Uint32 secs, int secs_n,
+ Uint32 usecs, int usecs_n)
+{
+ byte *p = sp;
+ Uint16 hdr;
+
+ ASSERT(*p == ERTS_MT_TIME_INC_BDY_TAG);
+ p++;
+
+ hdr = GET_UI16(p);
+
+ check_ui(&hdr, &p, secs, secs_n, UI32_MSB_EHF_MSK, UI32_MSB_EHF_SZ);
+ check_ui(&hdr, &p, usecs, usecs_n, UI32_MSB_EHF_MSK, UI32_MSB_EHF_SZ);
+
+ ASSERT(hdr == 0);
+ ASSERT(p == ep);
+
+}
+
+#endif /* #ifdef DEBUG */
+