📄 erl_trace.c
字号:
/* ``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 via the world wide web 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. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings * AB. All Rights Reserved.'' * * $Id$ *//* * Support functions for tracing. */#ifdef HAVE_CONFIG_H# include "config.h"#endif#include "sys.h"#include "erl_vm.h"#include "global.h"#include "erl_process.h"#include "big.h"#include "bif.h"#include "dist.h"#include "beam_bp.h"#include "error.h"#if 0#define DEBUG_PRINTOUTS#else#undef DEBUG_PRINTOUTS#endifextern Eterm beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */extern Eterm beam_return_trace[1]; /* OpCode(i_return_trace) *//* Pseudo export entries. Never filled in with data, only used to yield unique pointers of the correct type. */Export exp_send, exp_receive, exp_timeout;static Eterm system_seq_tracer;static Uint default_trace_flags;static Eterm default_tracer;static Eterm system_monitor;#ifdef HAVE_ERTS_NOW_CPUint erts_cpu_timestamp;#endifstatic erts_smp_mtx_t smq_mtx;static erts_smp_mtx_t sys_trace_mtx;enum ErtsSysMsgType { SYS_MSG_TYPE_UNDEFINED, SYS_MSG_TYPE_TRACE, SYS_MSG_TYPE_SEQTRACE, SYS_MSG_TYPE_SYSMON, SYS_MSG_TYPE_ERRLGR, SYS_MSG_TYPE_PROC_MSG};#ifdef ERTS_SMPstatic void enqueue_sys_msg_unlocked(enum ErtsSysMsgType type, Eterm from, Eterm to, Eterm msg, ErlHeapFragment *bp);static void enqueue_sys_msg(enum ErtsSysMsgType type, Eterm from, Eterm to, Eterm msg, ErlHeapFragment *bp);static void init_sys_msg_dispatcher(void);#endifvoid erts_init_trace(void) { erts_smp_mtx_init(&sys_trace_mtx, "sys_tracers");#ifdef HAVE_ERTS_NOW_CPU erts_cpu_timestamp = 0;#endif erts_bif_trace_init(); erts_system_monitor_clear(NULL); default_trace_flags = F_INITIAL_TRACE_FLAGS; default_tracer = NIL; system_seq_tracer = am_false;#ifdef ERTS_SMP init_sys_msg_dispatcher();#endif}static Eterm system_seq_tracer;voiderts_trace_check_exiting(Eterm exiting){ erts_smp_mtx_lock(&sys_trace_mtx); if (exiting == default_tracer) { default_tracer = NIL; default_trace_flags &= TRACEE_FLAGS;#ifdef DEBUG default_trace_flags |= F_INITIAL_TRACE_FLAGS;#endif } if (exiting == system_seq_tracer) {#ifdef DEBUG_PRINTOUTS erts_fprintf(stderr, "seq tracer %T exited\n", exiting);#endif system_seq_tracer = am_false; } if (exiting == system_monitor) {#ifdef ERTS_SMP system_monitor = NIL; /* Let the trace message dispatcher clear flags, etc */#else erts_system_monitor_clear(NULL);#endif } erts_smp_mtx_unlock(&sys_trace_mtx);}Etermerts_set_system_seq_tracer(Process *c_p, Uint32 c_p_locks, Eterm new){ Eterm old = THE_NON_VALUE; if (new != am_false) { if (!erts_pid2proc(c_p, c_p_locks, new, 0) && !erts_is_valid_tracer_port(new)) { return old; } } erts_smp_mtx_lock(&sys_trace_mtx); old = system_seq_tracer; system_seq_tracer = new;#ifdef DEBUG_PRINTOUTS erts_fprintf(stderr, "set seq tracer new=%T old=%T\n", new, old);#endif erts_smp_mtx_unlock(&sys_trace_mtx); return old;}Etermerts_get_system_seq_tracer(void){ Eterm st; erts_smp_mtx_lock(&sys_trace_mtx); st = system_seq_tracer;#ifdef DEBUG_PRINTOUTS erts_fprintf(stderr, "get seq tracer %T\n", st);#endif erts_smp_mtx_unlock(&sys_trace_mtx); return st;}static ERTS_INLINE voidget_default_tracing(Uint *flagsp, Eterm *tracerp){ if (!(default_trace_flags & TRACEE_FLAGS)) default_tracer = NIL; if (is_nil(default_tracer)) { default_trace_flags &= ~TRACEE_FLAGS; } else if (is_internal_pid(default_tracer)) { if (!erts_pid2proc(NULL, 0, default_tracer, 0)) { reset_tracer: default_trace_flags &= ~TRACEE_FLAGS; default_tracer = NIL; } } else { ASSERT(is_internal_port(default_tracer)); if (!erts_is_valid_tracer_port(default_tracer)) goto reset_tracer; } if (flagsp) *flagsp = default_trace_flags; if (tracerp) *tracerp = default_tracer;}voiderts_change_default_tracing(int setflags, Uint *flagsp, Eterm *tracerp){ erts_smp_mtx_lock(&sys_trace_mtx); if (flagsp) { if (setflags) default_trace_flags |= *flagsp; else default_trace_flags &= ~(*flagsp); } if (tracerp) default_tracer = *tracerp; get_default_tracing(flagsp, tracerp); erts_smp_mtx_unlock(&sys_trace_mtx);}voiderts_get_default_tracing(Uint *flagsp, Eterm *tracerp){ erts_smp_mtx_lock(&sys_trace_mtx); get_default_tracing(flagsp, tracerp); erts_smp_mtx_unlock(&sys_trace_mtx);}voiderts_set_system_monitor(Eterm monitor){ erts_smp_mtx_lock(&sys_trace_mtx); system_monitor = monitor; erts_smp_mtx_unlock(&sys_trace_mtx);}Etermerts_get_system_monitor(void){ Eterm monitor; erts_smp_mtx_lock(&sys_trace_mtx); monitor = system_monitor; erts_smp_mtx_unlock(&sys_trace_mtx); return monitor;}#ifdef HAVE_ERTS_NOW_CPU# define GET_NOW(m, s, u) \do { \ if (erts_cpu_timestamp) \ erts_get_now_cpu(m, s, u); \ else \ get_now(m, s, u); \} while (0)#else# define GET_NOW(m, s, u) do {get_now(m, s, u);} while (0)#endifstatic Eterm* patch_ts(Eterm tuple4, Eterm* hp);#ifdef ERTS_SMPstatic voiddo_send_to_port(Eterm to, Port* unused_port, Eterm from, enum ErtsSysMsgType type, Eterm message){ Uint sz = size_object(message); ErlHeapFragment *bp = new_message_buffer(sz); Uint *hp = bp->mem; Eterm msg = copy_struct(message, sz, &hp, &bp->off_heap); enqueue_sys_msg_unlocked(type, from, to, msg, bp);}#define WRITE_SYS_MSG_TO_PORT write_sys_msg_to_port#else#define WRITE_SYS_MSG_TO_PORT do_send_to_port#endifstatic voidWRITE_SYS_MSG_TO_PORT(Eterm unused_to, Port* trace_port, Eterm unused_from, enum ErtsSysMsgType unused_type, Eterm message) { byte *buffer; byte *ptr; unsigned size; size = encode_size_struct(message, TERM_TO_BINARY_DFLAGS); buffer = (byte *) erts_alloc(ERTS_ALC_T_TMP, size); ptr = buffer; erts_to_external_format(NULL, message, &ptr, NULL, NULL); if (!(ptr <= buffer+size)) { erl_exit(1, "Internal error in do_send_to_port: %d\n", ptr-buffer); }#ifndef ERTS_SMP if (!INVALID_TRACER_PORT(trace_port, trace_port->id)) {#endif dist_port_command(trace_port, buffer, ptr-buffer);#ifndef ERTS_SMP erts_port_release(trace_port); }#endif erts_free(ERTS_ALC_T_TMP, (void *) buffer);}#ifndef ERTS_SMP/* Send {trace_ts, Pid, out, 0, Timestamp} * followed by {trace_ts, Pid, in, 0, NewTimestamp} * * 'NewTimestamp' is fetched from GET_NOW() through patch_ts(). */static void do_send_schedfix_to_port(Port *trace_port, Eterm pid, Eterm timestamp) { Eterm local_heap[4+5+5]; Eterm message; Eterm *hp; Eterm mfarity; ASSERT(is_pid(pid)); ASSERT(is_tuple(timestamp)); ASSERT(*tuple_val(timestamp) == make_arityval(3)); hp = local_heap; mfarity = make_small(0); message = TUPLE5(hp, am_trace_ts, pid, am_out, mfarity, timestamp); /* Note, hp is deliberately NOT incremented since it will be reused */ do_send_to_port(trace_port->id, trace_port, pid, SYS_MSG_TYPE_UNDEFINED, message); message = TUPLE4(hp, am_trace_ts, pid, am_in, mfarity); hp += 5; hp = patch_ts(message, hp); do_send_to_port(trace_port->id, trace_port, pid, SYS_MSG_TYPE_UNDEFINED, message);}#endif/* If (c_p != NULL), a fake schedule out/in message pair will be sent, * if the driver so requests. * It is assumed that 'message' is not an 'out' message. * * 'c_p' is the currently executing process, "tracee" is the traced process * which 'message' concerns => if (*tracee_flags & F_TIMESTAMP), * 'message' must contain a timestamp. */static voidsend_to_port(Process *c_p, Eterm message, Eterm *tracer_pid, Uint *tracee_flags) { Port* trace_port;#ifndef ERTS_SMP Eterm ts, local_heap[4], *hp;#endif ASSERT(is_internal_port(*tracer_pid));#ifdef ERTS_SMP if (is_not_internal_port(*tracer_pid)) return; trace_port = NULL;#else if (is_not_internal_port(*tracer_pid)) goto invalid_tracer_port; trace_port = &erts_port[internal_port_index(*tracer_pid)]; if (INVALID_TRACER_PORT(trace_port, *tracer_pid)) { invalid_tracer_port: *tracee_flags &= ~TRACEE_FLAGS; *tracer_pid = NIL; return; } /* * Make a fake schedule only if the current process is traced * with 'running' and 'timestamp'. */ if (c_p == NULL || (! IS_TRACED_FL(c_p, F_TRACE_SCHED | F_TIMESTAMP))) {#endif do_send_to_port(*tracer_pid, trace_port, c_p ? c_p->id : NIL, SYS_MSG_TYPE_TRACE, message);#ifndef ERTS_SMP return; } /* * Note that the process being traced for some type of trace messages * (e.g. getting_linked) need not be the current process. That other * process might not have timestamps enabled. */ if (*tracee_flags & F_TIMESTAMP) { ASSERT(is_tuple(message)); hp = tuple_val(message); ts = hp[arityval(hp[0])]; } else { /* A fake schedule might be needed, * but this message does not contain a timestamp. * Create a dummy trace message with timestamp to be * passed to do_send_schedfix_to_port(). */ Uint ms,s,us; GET_NOW(&ms, &s, &us); hp = local_heap; ts = TUPLE3(hp, make_small(ms), make_small(s), make_small(us)); hp += 4; } trace_port->control_flags &= ~PORT_CONTROL_FLAG_HEAVY; do_send_to_port(*tracer_pid, trace_port, c_p ? c_p->id : NIL, SYS_MSG_TYPE_TRACE, message); if (trace_port->control_flags & PORT_CONTROL_FLAG_HEAVY) { /* The driver has just informed us that the last write took a * non-neglectible amount of time. * * We need to fake some trace messages to compensate for the time the * current process had to sacrifice for the writing of the previous * trace message. We pretend that the process got scheduled out * just after writning the real trace message, and now gets scheduled * in again. */ do_send_schedfix_to_port(trace_port, c_p->id, ts); }#endif}/* A fake schedule out/in message pair will be sent, * if the driver so requests. * If (timestamp == NIL), one is fetched from GET_NOW(). * * 'c_p' is the currently executing process, may be NULL. */static voidseq_trace_send_to_port(Process *c_p, Eterm seq_tracer, Eterm message, Eterm timestamp){ Port* trace_port;#ifndef ERTS_SMP Eterm ts, local_heap[4], *hp;#endif ASSERT(is_internal_port(seq_tracer));#ifdef ERTS_SMP if (is_not_internal_port(seq_tracer)) return; trace_port = NULL;#else if (is_not_internal_port(seq_tracer)) goto invalid_tracer_port; trace_port = &erts_port[internal_port_index(seq_tracer)]; if (INVALID_TRACER_PORT(trace_port, seq_tracer)) { invalid_tracer_port: system_seq_tracer = am_false; return; } if (c_p == NULL || (! IS_TRACED_FL(c_p, F_TRACE_SCHED | F_TIMESTAMP))) {#endif do_send_to_port(seq_tracer, trace_port, c_p ? c_p->id : NIL, SYS_MSG_TYPE_SEQTRACE, message);#ifndef ERTS_SMP return; } /* Make a fake schedule only if the current process is traced * with 'running' and 'timestamp'. */ if (timestamp != NIL) { ts = timestamp; } else { /* A fake schedule might be needed, * but this message does not contain a timestamp. * Create a dummy trace message with timestamp to be * passed to do_send_schedfix_to_port(). */ Uint ms,s,us; GET_NOW(&ms, &s, &us); hp = local_heap; ts = TUPLE3(hp, make_small(ms), make_small(s), make_small(us)); hp += 4; } trace_port->control_flags &= ~PORT_CONTROL_FLAG_HEAVY; do_send_to_port(seq_tracer, trace_port, c_p ? c_p->id : NIL, SYS_MSG_TYPE_SEQTRACE, message); if (trace_port->control_flags & PORT_CONTROL_FLAG_HEAVY) { /* The driver has just informed us that the last write took a * non-neglectible amount of time. * * We need to fake some trace messages to compensate for the time the * current process had to sacrifice for the writing of the previous * trace message. We pretend that the process got scheduled out * just after writning the real trace message, and now gets scheduled * in again. */ do_send_schedfix_to_port(trace_port, c_p->id, ts); }#endif}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -