⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 erl_trace.c

📁 OTP是开放电信平台的简称
💻 C
📖 第 1 页 / 共 5 页
字号:
/* ``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 + -