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);
}