📄 fasttrap.c
字号:
* Tracepoints whose provider is now defunct are also considered * defunct. */again: mutex_enter(&bucket->ftb_mtx); for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) { if (tp->ftt_pid != pid || tp->ftt_pc != pc || tp->ftt_prov->ftp_defunct) continue; /* * Now that we've found a matching tracepoint, it would be * a decent idea to confirm that the tracepoint is still * enabled and the trap instruction hasn't been overwritten. * Since this is a little hairy, we'll punt for now. */ /* * This can't be the first interested probe. We don't have * to worry about another thread being in the midst of * deleting this tracepoint (which would be the only valid * reason for a tracepoint to have no interested probes) * since we're holding P_PR_LOCK for this process. */ ASSERT(tp->ftt_ids != NULL || tp->ftt_retids != NULL); if (probe->ftp_type == DTFTP_RETURN || probe->ftp_type == DTFTP_POST_OFFSETS) { id->fti_next = tp->ftt_retids; membar_producer(); tp->ftt_retids = id; membar_producer(); } else { id->fti_next = tp->ftt_ids; membar_producer(); tp->ftt_ids = id; membar_producer(); } mutex_exit(&bucket->ftb_mtx); if (new_tp != NULL) { new_tp->ftt_ids = NULL; new_tp->ftt_retids = NULL; } return (0); } /* * If we have a good tracepoint ready to go, install it now while * we have the lock held and no one can screw with us. */ if (new_tp != NULL) { int rc; new_tp->ftt_next = bucket->ftb_data; membar_producer(); bucket->ftb_data = new_tp; membar_producer(); mutex_exit(&bucket->ftb_mtx); /* * Activate the tracepoint in the isa-specific manner. */ if (fasttrap_tracepoint_install(p, new_tp) != 0) { rc = 1; } else { /* * Increment the count of the number of tracepoints * active in the victim process. */ ASSERT(p->p_proc_flag & P_PR_LOCK); p->p_dtrace_count++; rc = 0; } return (rc); } mutex_exit(&bucket->ftb_mtx); /* * Initialize the tracepoint that's been preallocated with the probe. */ new_tp = probe->ftp_tps[index].fit_tp; ASSERT(new_tp->ftt_pid == pid); ASSERT(new_tp->ftt_pc == pc); ASSERT(new_tp->ftt_prov == probe->ftp_prov); ASSERT(new_tp->ftt_ids == NULL); ASSERT(new_tp->ftt_retids == NULL); if (probe->ftp_type == DTFTP_RETURN || probe->ftp_type == DTFTP_POST_OFFSETS) { id->fti_next = NULL; new_tp->ftt_retids = id; } else { id->fti_next = NULL; new_tp->ftt_ids = id; } /* * If the isa-dependent initialization goes to plan, go back to the * beginning and try to install this freshly made tracepoint. */ if (fasttrap_tracepoint_init(p, probe, new_tp, pc) == 0) goto again; new_tp->ftt_ids = NULL; new_tp->ftt_retids = NULL; return (1);}static voidfasttrap_tracepoint_disable(proc_t *p, fasttrap_probe_t *probe, uint_t index){ fasttrap_bucket_t *bucket; fasttrap_provider_t *provider = probe->ftp_prov; fasttrap_tracepoint_t **pp, *tp; fasttrap_id_t *id, **idp; pid_t pid; uintptr_t pc; ASSERT(index < probe->ftp_ntps); pid = probe->ftp_pid; pc = probe->ftp_tps[index].fit_tp->ftt_pc; ASSERT(probe->ftp_tps[index].fit_tp->ftt_pid == pid); /* * Find the tracepoint and make sure that our id is one of the * ones registered with it. */ bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, pc)]; mutex_enter(&bucket->ftb_mtx); for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) { if (tp->ftt_pid == pid && tp->ftt_pc == pc && tp->ftt_prov == provider) break; } /* * If we somehow lost this tracepoint, we're in a world of hurt. */ ASSERT(tp != NULL); if (probe->ftp_type == DTFTP_RETURN || probe->ftp_type == DTFTP_POST_OFFSETS) { ASSERT(tp->ftt_retids != NULL); idp = &tp->ftt_retids; } else { ASSERT(tp->ftt_ids != NULL); idp = &tp->ftt_ids; } while ((*idp)->fti_probe != probe) { idp = &(*idp)->fti_next; ASSERT(*idp != NULL); } id = *idp; *idp = id->fti_next; membar_producer(); ASSERT(id->fti_probe == probe); /* * If there are other registered enablings of this tracepoint, we're * all done, but if this was the last probe assocated with this * this tracepoint, we need to remove and free it. */ if (tp->ftt_ids != NULL || tp->ftt_retids != NULL) { /* * If the current probe's tracepoint is in use, swap it * for an unused tracepoint. */ if (tp == probe->ftp_tps[index].fit_tp) { fasttrap_probe_t *tmp_probe; fasttrap_tracepoint_t **tmp_tp; uint_t tmp_index; if (tp->ftt_ids != NULL) { tmp_probe = tp->ftt_ids->fti_probe; tmp_index = FASTTRAP_ID_INDEX(tp->ftt_ids); tmp_tp = &tmp_probe->ftp_tps[tmp_index].fit_tp; } else { tmp_probe = tp->ftt_retids->fti_probe; tmp_index = FASTTRAP_ID_INDEX(tp->ftt_retids); tmp_tp = &tmp_probe->ftp_tps[tmp_index].fit_tp; } ASSERT(*tmp_tp != NULL); ASSERT(*tmp_tp != probe->ftp_tps[index].fit_tp); ASSERT((*tmp_tp)->ftt_ids == NULL); ASSERT((*tmp_tp)->ftt_retids == NULL); probe->ftp_tps[index].fit_tp = *tmp_tp; *tmp_tp = tp; } mutex_exit(&bucket->ftb_mtx); /* * Tag the modified probe with the generation in which it was * changed. */ probe->ftp_gen = fasttrap_mod_gen; return; } mutex_exit(&bucket->ftb_mtx); /* * We can't safely remove the tracepoint from the set of active * tracepoints until we've actually removed the fasttrap instruction * from the process's text. We can, however, operate on this * tracepoint secure in the knowledge that no other thread is going to * be looking at it since we hold P_PR_LOCK on the process if it's * live or we hold the provider lock on the process if it's dead and * gone. */ /* * We only need to remove the actual instruction if we're looking * at an existing process */ if (p != NULL) { /* * If we fail to restore the instruction we need to kill * this process since it's in a completely unrecoverable * state. */ if (fasttrap_tracepoint_remove(p, tp) != 0) fasttrap_sigtrap(p, NULL, pc); /* * Decrement the count of the number of tracepoints active * in the victim process. */ ASSERT(p->p_proc_flag & P_PR_LOCK); p->p_dtrace_count--; } /* * Remove the probe from the hash table of active tracepoints. */ mutex_enter(&bucket->ftb_mtx); pp = (fasttrap_tracepoint_t **)&bucket->ftb_data; ASSERT(*pp != NULL); while (*pp != tp) { pp = &(*pp)->ftt_next; ASSERT(*pp != NULL); } *pp = tp->ftt_next; membar_producer(); mutex_exit(&bucket->ftb_mtx); /* * Tag the modified probe with the generation in which it was changed. */ probe->ftp_gen = fasttrap_mod_gen;}typedef int fasttrap_probe_f(struct regs *);static voidfasttrap_enable_common(int *count, fasttrap_probe_f **fptr, fasttrap_probe_f *f, fasttrap_probe_f **fptr2, fasttrap_probe_f *f2){ /* * We don't have to play the rw lock game here because we're * providing something rather than taking something away -- * we can be sure that no threads have tried to follow this * function pointer yet. */ mutex_enter(&fasttrap_count_mtx); if (*count == 0) { ASSERT(*fptr == NULL); *fptr = f; if (fptr2 != NULL) *fptr2 = f2; } ASSERT(*fptr == f); ASSERT(fptr2 == NULL || *fptr2 == f2); (*count)++; mutex_exit(&fasttrap_count_mtx);}static voidfasttrap_disable_common(int *count, fasttrap_probe_f **fptr, fasttrap_probe_f **fptr2){ ASSERT(MUTEX_HELD(&cpu_lock)); mutex_enter(&fasttrap_count_mtx); (*count)--; ASSERT(*count >= 0); if (*count == 0) { cpu_t *cur, *cpu = CPU; for (cur = cpu->cpu_next_onln; cur != cpu; cur = cur->cpu_next_onln) { rw_enter(&cur->cpu_ft_lock, RW_WRITER); } *fptr = NULL; if (fptr2 != NULL) *fptr2 = NULL; for (cur = cpu->cpu_next_onln; cur != cpu; cur = cur->cpu_next_onln) { rw_exit(&cur->cpu_ft_lock); } } mutex_exit(&fasttrap_count_mtx);}/*ARGSUSED*/static voidfasttrap_enable(void *arg, dtrace_id_t id, void *parg){ /* * Enable the probe that corresponds to statically placed trace * points which have not explicitly been placed in the process's text * by the fasttrap provider. */ ASSERT(arg == NULL); ASSERT(id == fasttrap_probe_id); fasttrap_enable_common(&fasttrap_count, &dtrace_fasttrap_probe_ptr, fasttrap_probe, NULL, NULL);}/*ARGSUSED*/static voidfasttrap_pid_enable(void *arg, dtrace_id_t id, void *parg){ fasttrap_probe_t *probe = parg; proc_t *p; int i; ASSERT(probe != NULL); ASSERT(!probe->ftp_enabled); ASSERT(id == probe->ftp_id); ASSERT(MUTEX_HELD(&cpu_lock)); /* * Increment the count of enabled probes on this probe's provider; * the provider can't go away while the probe still exists. We * must increment this even if we aren't able to properly enable * this probe. */ mutex_enter(&probe->ftp_prov->ftp_mtx); probe->ftp_prov->ftp_rcount++; mutex_exit(&probe->ftp_prov->ftp_mtx); /* * Bail out if we can't find the process for this probe or its * provider is defunct (meaning it was valid in a previously exec'ed * incarnation of this address space). The provider can't go away * while we're in this code path. */ if (probe->ftp_prov->ftp_defunct || (p = sprlock(probe->ftp_pid)) == NULL) return; ASSERT(!(p->p_flag & SVFORK)); mutex_exit(&p->p_lock); /* * We have to enable the trap entry before any user threads have * the chance to execute the trap instruction we're about to place * in their process's text. */ fasttrap_enable_common(&fasttrap_pid_count, &dtrace_pid_probe_ptr, fasttrap_pid_probe, &dtrace_return_probe_ptr, fasttrap_return_probe); /* * Enable all the tracepoints and add this probe's id to each * tracepoint's list of active probes. */ for (i = 0; i < probe->ftp_ntps; i++) { if (fasttrap_tracepoint_enable(p, probe, i) != 0) { /* * Back up and pull out all the tracepoints we've * created so far for this probe. */ while (--i >= 0) { fasttrap_tracepoint_disable(p, probe, i); } mutex_enter(&p->p_lock); sprunlock(p); /* * Since we're not actually enabling this probe, * drop our reference on the trap table entry. */ fasttrap_disable_common(&fasttrap_pid_count, &dtrace_pid_probe_ptr, &dtrace_return_probe_ptr); return; } } mutex_enter(&p->p_lock); sprunlock(p); probe->ftp_enabled = 1;}/*ARGSUSED*/static voidfasttrap_disable(void *arg, dtrace_id_t id, void *parg){ /* * Disable the probe the corresponds to statically placed trace * points. */ ASSERT(arg == NULL); ASSERT(id == fasttrap_probe_id); ASSERT(MUTEX_HELD(&cpu_lock)); fasttrap_disable_common(&fasttrap_count, &dtrace_fasttrap_probe_ptr, NULL);}/*ARGSUSED*/static voidfasttrap_pid_disable(void *arg, dtrace_id_t id, void *parg){ fasttrap_probe_t *probe = parg; fasttrap_provider_t *provider = probe->ftp_prov; proc_t *p; int i, whack = 0; if (!probe->ftp_enabled) { mutex_enter(&provider->ftp_mtx); provider->ftp_rcount--; ASSERT(provider->ftp_rcount >= 0); mutex_exit(&provider->ftp_mtx); return; } ASSERT(id == probe->ftp_id); /* * We won't be able to acquire a /proc-esque lock on the process * iff the process is dead and gone. In this case, we rely on the * provider lock as a point of mutual exclusion to prevent other * DTrace consumers from disabling this probe. */ if ((p = sprlock(probe->ftp_pid)) != NULL) { ASSERT(!(p->p_flag & SVFORK)); mutex_exit(&p->p_lock); } mutex_enter(&provider->ftp_mtx); /* * Disable all the associated tracepoints. */ for (i = 0; i < probe->ftp_ntps; i++) { fasttrap_tracepoint_disable(p, probe, i); } ASSERT(provider->ftp_rcount > 0); provider->ftp_rcount--; if (p != NULL) { /* * Even though we may not be able to remove it entirely, we * mark this defunct provider to get a chance to remove some * of the associated probes. */ if (provider->ftp_defunct && !provider->ftp_marked) whack = provider->ftp_marked = 1; mutex_exit(&provider->ftp_mtx); mutex_enter(&p->p_lock);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -