Eterm erts_internal_trace_3()

in erts/emulator/beam/erl_bif_trace.c [526:780]


Eterm erts_internal_trace_3(BIF_ALIST_3)
{
    Process* p = BIF_P;
    Eterm pid_spec = BIF_ARG_1;
    Eterm how = BIF_ARG_2;
    Eterm list = BIF_ARG_3;
    int on;
    ErtsTracer tracer = erts_tracer_nil;
    int matches = 0;
    Uint mask = 0;
    int cpu_ts = 0;
    int system_blocked = 0;

    if (! erts_trace_flags(list, &mask, &tracer, &cpu_ts)) {
	BIF_ERROR(p, BADARG);
    }

    if (!erts_try_seize_code_write_permission(BIF_P)) {
	ERTS_BIF_YIELD3(BIF_TRAP_EXPORT(BIF_erts_internal_trace_3),
                        BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
    }

    switch (how) {
    case am_false: 
	on = 0; 
	break;
    case am_true: 
	on = 1;
        if (ERTS_TRACER_IS_NIL(tracer))
            tracer = erts_term_to_tracer(am_tracer, p->common.id);

        if (tracer == THE_NON_VALUE) {
            tracer = erts_tracer_nil;
            goto error;
        }

	break;
    default: 
	goto error;
    }

    /*
     * Set/reset the call trace flag for the given Pids.
     */

    if (is_port(pid_spec)) {
	Port *tracee_port;

#ifdef HAVE_ERTS_NOW_CPU
	if (cpu_ts) {
	    goto error;
	}
#endif

	tracee_port = erts_id2port_sflgs(pid_spec,
					 p,
					 ERTS_PROC_LOCK_MAIN,
					 ERTS_PORT_SFLGS_INVALID_LOOKUP);

	if (!tracee_port)
	    goto error;

        if (start_trace(p, tracer, &tracee_port->common, on, mask)) {
	    erts_port_release(tracee_port);
	    goto already_traced;
        }
        erts_port_release(tracee_port);
        matches = 1;
    } else if (is_pid(pid_spec)) {
	Process *tracee_p;

#ifdef HAVE_ERTS_NOW_CPU
	if (cpu_ts) {
	    goto error;
	}
#endif
	/* Check that the tracee is not dead, not tracing 
	 * and not about to be tracing.
	 */

	tracee_p = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN,
				 pid_spec, ERTS_PROC_LOCKS_ALL);
	if (!tracee_p)
	    goto error;

        if (start_trace(tracee_p, tracer, &tracee_p->common, on, mask)) {
	    erts_proc_unlock(tracee_p,
				 (tracee_p == p
				  ? ERTS_PROC_LOCKS_ALL_MINOR
				  : ERTS_PROC_LOCKS_ALL));
	    goto already_traced;
        }
        erts_proc_unlock(tracee_p,
			     (tracee_p == p
			      ? ERTS_PROC_LOCKS_ALL_MINOR
			      : ERTS_PROC_LOCKS_ALL));

	matches = 1;
    } else {
	int ok = 0;

#ifdef HAVE_ERTS_NOW_CPU
	if (cpu_ts) {
	    if (pid_spec == am_all) {
		if (on) {
		    if (!erts_cpu_timestamp) {
#ifdef HAVE_CLOCK_GETTIME_CPU_TIME
			/* 
			   Perhaps clock_gettime was found during config
			   on a different machine than this. We check
			   if it works here and now, then don't bother 
			   about checking return value for error later. 
			*/
			{
			    SysCpuTime start, stop;
			    SysTimespec tp;
			    int i;
			    
			    if (sys_get_cputime(start, tp) < 0)
				goto error;
			    start = ((SysCpuTime)tp.tv_sec * 1000000000LL) + 
				    (SysCpuTime)tp.tv_nsec;
			    for (i = 0; i < 100; i++)
				sys_get_cputime(stop, tp);
			    stop = ((SysCpuTime)tp.tv_sec * 1000000000LL) + 
				   (SysCpuTime)tp.tv_nsec;
			    if (start == 0) goto error;
			    if (start == stop) goto error;
			}
#else /* HAVE_GETHRVTIME */
			if (erts_start_now_cpu() < 0) {
			    goto error;
			}
#endif /* HAVE_CLOCK_GETTIME_CPU_TIME */
			erts_cpu_timestamp = !0;
		    }
		}
	    } else {
		goto error;
	    }
	}
#endif
	
	if (pid_spec == am_all || pid_spec == am_existing ||
            pid_spec == am_ports || pid_spec == am_processes ||
            pid_spec == am_existing_ports || pid_spec == am_existing_processes
            ) {
	    int i;
	    int procs = 0;
	    int ports = 0;
	    int mods = 0;

	    if (mask & (ERTS_PROC_TRACEE_FLAGS & ~ERTS_TRACEE_MODIFIER_FLAGS))
		procs = pid_spec != am_ports && pid_spec != am_existing_ports;
	    if (mask & (ERTS_PORT_TRACEE_FLAGS & ~ERTS_TRACEE_MODIFIER_FLAGS))
		ports = pid_spec != am_processes && pid_spec != am_existing_processes;
	    if (mask & ERTS_TRACEE_MODIFIER_FLAGS) {
                if (pid_spec == am_ports || pid_spec == am_existing_ports)
                    ports = 1;
                else if (pid_spec == am_processes || pid_spec == am_existing_processes)
                    procs = 1;
                else
                    mods = 1;
            }

	    erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
	    erts_thr_progress_block();
	    system_blocked = 1;

	    ok = 1;
	    if (procs || mods) {
		int max = erts_ptab_max(&erts_proc);
		/* tracing of processes */
		for (i = 0; i < max; i++) {
		    Process* tracee_p = erts_pix2proc(i);
		    if (! tracee_p) 
			continue;
                    if (!start_trace(p, tracer, &tracee_p->common, on, mask))
                        matches++;
		}
	    }
	    if (ports || mods) {
		int max = erts_ptab_max(&erts_port);
		/* tracing of ports */
		for (i = 0; i < max; i++) {
		    erts_aint32_t state;
		    Port *tracee_port = erts_pix2port(i);
		    if (!tracee_port)
			continue;
		    state = erts_atomic32_read_nob(&tracee_port->state);
		    if (state & ERTS_PORT_SFLGS_DEAD)
			continue;
                    if (!start_trace(p, tracer, &tracee_port->common, on, mask))
                        matches++;
		}
	    }
	}

	if (pid_spec == am_all || pid_spec == am_new
            || pid_spec == am_ports || pid_spec == am_processes
            || pid_spec == am_new_ports || pid_spec == am_new_processes
            ) {

	    ok = 1;
            if (mask & ERTS_PROC_TRACEE_FLAGS &&
                pid_spec != am_ports && pid_spec != am_new_ports)
                erts_change_default_proc_tracing(
                    on, mask & ERTS_PROC_TRACEE_FLAGS, tracer);
            if (mask & ERTS_PORT_TRACEE_FLAGS &&
                pid_spec != am_processes && pid_spec != am_new_processes)
                erts_change_default_port_tracing(
                    on, mask & ERTS_PORT_TRACEE_FLAGS, tracer);

#ifdef HAVE_ERTS_NOW_CPU
	    if (cpu_ts && !on) {
		/* cpu_ts => pid_spec == am_all */
		if (erts_cpu_timestamp) {
#ifdef HAVE_GETHRVTIME
		    erts_stop_now_cpu();
#endif
		    erts_cpu_timestamp = 0;
		}
	    }
#endif
	}

	if (!ok)
	    goto error;
    }

    if (system_blocked) {
	erts_thr_progress_unblock();
	erts_proc_lock(p, ERTS_PROC_LOCK_MAIN);
    }
    erts_release_code_write_permission();
    ERTS_TRACER_CLEAR(&tracer);

    BIF_RET(make_small(matches));

 already_traced:
    erts_send_error_to_logger_str(p->group_leader,
				  "** can only have one tracer per process\n");

 error:

    ERTS_TRACER_CLEAR(&tracer);

    if (system_blocked) {
	erts_thr_progress_unblock();
	erts_proc_lock(p, ERTS_PROC_LOCK_MAIN);
    }
    erts_release_code_write_permission();

    BIF_ERROR(p, BADARG);
}