| 1 | #include <log.h> |
| 2 | #include <math/min_max.h> |
| 3 | #include <thread/thread.h> |
| 4 | #include <thread/thread_diag.h> |
| 5 | |
| 6 | struct thread_diag *thread_diag(struct thread *t) { |
| 7 | if (!t || !(thread_get_flags(t) & THREAD_FLAG_DIAG)) |
| 8 | return NULL; |
| 9 | |
| 10 | return t->private; |
| 11 | } |
| 12 | |
| 13 | bool thread_diag_attach(struct thread *t, struct thread_diag *d) { |
| 14 | if (!t || !d) |
| 15 | return false; |
| 16 | |
| 17 | if (t->private) |
| 18 | return false; |
| 19 | |
| 20 | t->private = d; |
| 21 | thread_or_flags(t, flags: THREAD_FLAG_DIAG); |
| 22 | return true; |
| 23 | } |
| 24 | |
| 25 | void thread_diag_detach(struct thread *t) { |
| 26 | if (!t || !(thread_get_flags(t) & THREAD_FLAG_DIAG)) |
| 27 | return; |
| 28 | |
| 29 | thread_and_flags(t, flags: ~THREAD_FLAG_DIAG); |
| 30 | t->private = NULL; |
| 31 | } |
| 32 | |
| 33 | void thread_diag_record_arm(struct thread *t, const char *site, void *ra, |
| 34 | void *object, enum thread_state state, |
| 35 | enum thread_wait_type type, uint8_t reason) { |
| 36 | struct thread_diag *d = thread_diag(t); |
| 37 | if (!d) |
| 38 | return; |
| 39 | |
| 40 | d->wait_arm_total++; |
| 41 | |
| 42 | struct thread_wait_arm *newest = |
| 43 | d->wait_arm_count |
| 44 | ? &d->wait_trace[(d->wait_arm_count - 1) % THREAD_WAIT_TRACE_DEPTH] |
| 45 | : NULL; |
| 46 | |
| 47 | if (newest && newest->object == object && |
| 48 | newest->block_count == t->wait_block_count && |
| 49 | newest->wait_type == (uint8_t) type && |
| 50 | newest->state == (uint8_t) state && newest->reason == reason) { |
| 51 | newest->last_epoch = atomic_load_rlx(&t->wait_epoch); |
| 52 | newest->last_ms = time_get_ms(); |
| 53 | newest->repeats++; |
| 54 | return; |
| 55 | } |
| 56 | |
| 57 | struct thread_wait_arm *e = |
| 58 | &d->wait_trace[d->wait_arm_count % THREAD_WAIT_TRACE_DEPTH]; |
| 59 | |
| 60 | e->site = site; |
| 61 | e->ra = ra; |
| 62 | e->object = object; |
| 63 | e->first_epoch = atomic_load_rlx(&t->wait_epoch); |
| 64 | e->last_epoch = e->first_epoch; |
| 65 | e->repeats = 1; |
| 66 | e->last_ms = time_get_ms(); |
| 67 | e->state = (uint8_t) state; |
| 68 | e->wait_type = (uint8_t) type; |
| 69 | e->reason = reason; |
| 70 | e->block_count = t->wait_block_count; |
| 71 | |
| 72 | d->wait_arm_count++; |
| 73 | } |
| 74 | |
| 75 | void thread_diag_apc_deliver_enter(struct thread *t, void *ra) { |
| 76 | struct thread_diag *d = thread_diag(t); |
| 77 | if (!d) |
| 78 | return; |
| 79 | |
| 80 | d->apc_deliver_entries++; |
| 81 | d->apc_last_deliver_ra = ra; |
| 82 | } |
| 83 | |
| 84 | void thread_diag_apc_deliver_batch(struct thread *t, uint64_t drained) { |
| 85 | struct thread_diag *d = thread_diag(t); |
| 86 | if (!d) |
| 87 | return; |
| 88 | |
| 89 | if (drained > d->apc_deliver_max) |
| 90 | d->apc_deliver_max = drained; |
| 91 | } |
| 92 | |
| 93 | static const char *thread_wait_type_str(uint8_t type) { |
| 94 | switch (type) { |
| 95 | case THREAD_WAIT_NONE: return "NONE" ; |
| 96 | case THREAD_WAIT_UNINTERRUPTIBLE: return "UNINT" ; |
| 97 | case THREAD_WAIT_INTERRUPTIBLE: return "INTR" ; |
| 98 | default: return "?" ; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | void thread_dump_wait_trace(struct thread *t, const char *role, size_t idx, |
| 103 | uint64_t max_arms) { |
| 104 | struct thread_diag *d = thread_diag(t); |
| 105 | if (!d) |
| 106 | return; |
| 107 | |
| 108 | uint64_t count = d->wait_arm_count; |
| 109 | |
| 110 | log_msg(LOG_ERROR, " %s[%zu] arms=%lu (%lu distinct)" , role, idx, |
| 111 | d->wait_arm_total, count); |
| 112 | |
| 113 | log_msg(LOG_ERROR, |
| 114 | " %s[%zu] apc: entries=%lu max_burst=%lu deliver_ra=%p" , role, |
| 115 | idx, d->apc_deliver_entries, d->apc_deliver_max, |
| 116 | d->apc_last_deliver_ra); |
| 117 | |
| 118 | uint64_t depth = MIN(max_arms, THREAD_WAIT_TRACE_DEPTH); |
| 119 | uint64_t shown = MIN(count, depth); |
| 120 | |
| 121 | for (uint64_t i = 0; i < shown; i++) { |
| 122 | uint64_t seq = count - 1 - i; |
| 123 | struct thread_wait_arm *e = |
| 124 | &d->wait_trace[seq % THREAD_WAIT_TRACE_DEPTH]; |
| 125 | |
| 126 | log_msg(LOG_ERROR, " %s[%zu] arm#%lu %s ra=%p x%lu" , role, idx, seq, |
| 127 | e->site ? e->site : "?" , e->ra, e->repeats); |
| 128 | |
| 129 | log_msg(LOG_ERROR, |
| 130 | " %s[%zu] -> state=%d %s object=%p reason=%u " |
| 131 | "epoch=%lu..%lu last=%lums blocks=%u" , |
| 132 | role, idx, (int) e->state, thread_wait_type_str(e->wait_type), |
| 133 | e->object, (unsigned) e->reason, e->first_epoch, e->last_epoch, |
| 134 | e->last_ms, e->block_count); |
| 135 | } |
| 136 | } |
| 137 | |