1#ifdef DEBUG_LOCK_CHK
2
3#include <console/crash.h>
4#include <console/printf.h>
5#include <ndjson.h>
6#include <nightmare/nightmare.h>
7#include <stdatomic.h>
8#include <string.h>
9
10#include "lock_chk_internal.h"
11
12NDJSON_DECLARE(lock_chk_finding, NDJSON_SECTION_LOCK_CHK, NDJSON_KIND_FINDING,
13 1, NDJSON_STR(kind), NDJSON_STR(sig), NDJSON_STR(file),
14 NDJSON_U64(line), NDJSON_STR(class), NDJSON_STR(msg),
15 NDJSON_STR(mode), NDJSON_U64(cycle_len),
16 NDJSON_STR(capacity_pool), NDJSON_U64(capacity_used),
17 NDJSON_U64(capacity_limit));
18
19static const char *lock_chk_fail_kind_name(enum lock_chk_failure_kind kind) {
20 switch (kind) {
21 case LOCK_CHK_FAIL_CYCLE: return "cycle";
22 case LOCK_CHK_FAIL_RECURSION: return "recursion";
23 case LOCK_CHK_FAIL_RELEASE: return "release";
24 case LOCK_CHK_FAIL_CONTEXT: return "context";
25 case LOCK_CHK_FAIL_THREAD_EXIT: return "thread_exit";
26 case LOCK_CHK_FAIL_SPIN_ORDER: return "spin_order";
27 case LOCK_CHK_FAIL_CAPACITY: return "capacity";
28 case LOCK_CHK_FAIL_UNINITIALIZED: return "uninitialized";
29 case LOCK_CHK_FAIL_NOT_HELD: return "not_held";
30 case LOCK_CHK_FAIL_UNEXPECTED_HELD: return "unexpected_held";
31 }
32 return "unknown";
33}
34
35static const char *lock_chk_mode_name(enum lock_chk_mode mode) {
36 if (mode == LOCK_CHK_MODE_EXCLUSIVE)
37 return "exclusive";
38 if (mode == LOCK_CHK_MODE_SHARED)
39 return "shared";
40 return "";
41}
42
43static uint64_t
44lock_chk_calc_generic_sig(const struct lock_chk_failure *failure) {
45 uint64_t signature = HASH_FNV1A_64_OFFSET_BASIS;
46 signature =
47 lock_chk_hash_string(signature, lock_chk_fail_kind_name(failure->kind));
48 if (failure->class != NULL) {
49 signature = lock_chk_hash_string(signature, failure->class->name);
50 signature = lock_chk_hash_string(signature, failure->class->file);
51 signature = lock_chk_hash_bytes(signature, &failure->class->line,
52 sizeof(failure->class->line));
53 }
54 if (failure->site != NULL) {
55 signature = lock_chk_hash_string(signature, failure->site->file);
56 signature = lock_chk_hash_bytes(signature, &failure->site->line,
57 sizeof(failure->site->line));
58 }
59 signature = lock_chk_hash_bytes(signature, &failure->subclass,
60 sizeof(failure->subclass));
61 return lock_chk_hash_bytes(signature, &failure->mode,
62 sizeof(failure->mode));
63}
64
65static uint64_t
66lock_chk_failure_signature(const struct lock_chk_failure *failure) {
67 if (failure->signature != 0)
68 return failure->signature;
69 if (failure->kind == LOCK_CHK_FAIL_CYCLE && failure->cycle_len != 0)
70 return lock_chk_calc_canonical_cycle_sig(failure->cycle_hops,
71 failure->cycle_len);
72 return lock_chk_calc_generic_sig(failure);
73}
74
75static void lock_chk_emit_finding(const struct lock_chk_failure *failure,
76 const char *kind, const char *signature) {
77 ndjson_emit(lock_chk_finding, .kind = kind, .sig = signature,
78 .file = failure->site ? failure->site->file : "",
79 .line = failure->site ? failure->site->line : 0,
80 .class = failure->class ? failure->class->name : "",
81 .msg = failure->msg, .mode = lock_chk_mode_name(failure->mode),
82 .cycle_len = failure->cycle_len,
83 .capacity_pool =
84 failure->capacity_pool ? failure->capacity_pool : "",
85 .capacity_used = failure->capacity_used,
86 .capacity_limit = failure->capacity_limit);
87}
88
89static void lock_chk_report_degradation(const struct lock_chk_failure *failure,
90 uint64_t signature,
91 const char *signature_text) {
92 static _Atomic bool emitted = false;
93 if (atomic_exchange_explicit(&emitted, true, memory_order_relaxed))
94 return;
95
96 printf_unlocked("\n*** LOCK_CHK WARNING: Capacity exhausted for pool '%s' "
97 "(%u/%u) - entering DEGRADED mode ***\n\n",
98 failure->capacity_pool ? failure->capacity_pool
99 : "<unknown>",
100 failure->capacity_used, failure->capacity_limit);
101 lock_chk_emit_finding(failure, "capacity_degraded", signature_text);
102 nightmare_request_external_fail("lock_chk_degraded", signature, "%s",
103 failure->msg);
104}
105
106static void lock_chk_print_cycle(const struct lock_chk_failure *failure) {
107 if (failure->kind != LOCK_CHK_FAIL_CYCLE || failure->cycle_len == 0)
108 return;
109
110 printf_unlocked("Cycle path (%u hops%s):\n", failure->cycle_len,
111 failure->cycle_truncated ? ", truncated" : "");
112 for (uint16_t i = 0; i < failure->cycle_len; i++) {
113 const struct lock_chk_cycle_hop *hop = &failure->cycle_hops[i];
114 printf_unlocked(
115 " [%u] '%s' (%s:%u sc %u) [%s]\n", i,
116 hop->from_class ? hop->from_class->name : "<unknown>",
117 hop->from_class ? hop->from_class->file : "?",
118 hop->from_class ? hop->from_class->line : 0, hop->from_subclass,
119 hop->from_mode == LOCK_CHK_MODE_EXCLUSIVE ? "EXCLUSIVE" : "SHARED");
120 printf_unlocked(
121 " -> '%s' (%s:%u sc %u) [%s] at %s:%u\n",
122 hop->to_class ? hop->to_class->name : "<unknown>",
123 hop->to_class ? hop->to_class->file : "?",
124 hop->to_class ? hop->to_class->line : 0, hop->to_subclass,
125 hop->to_mode == LOCK_CHK_MODE_EXCLUSIVE ? "EXCLUSIVE" : "SHARED",
126 hop->site ? hop->site->file : "?", hop->site ? hop->site->line : 0);
127 }
128}
129
130static void lock_chk_print_failure(const struct lock_chk_failure *failure,
131 const char *signature) {
132 printf_unlocked("\n========================================================"
133 "========================\n");
134 printf_unlocked("LOCK_CHK VIOLATION: %s\n", failure->msg);
135 printf_unlocked("Signature: 0x%s\n", signature);
136 if (failure->site != NULL)
137 printf_unlocked("Location: %s:%u\n", failure->site->file,
138 failure->site->line);
139 if (failure->class != NULL)
140 printf_unlocked("Lock Class: '%s' (%s:%u subclass %u)\n",
141 failure->class->name, failure->class->file,
142 failure->class->line, failure->subclass);
143
144 lock_chk_print_cycle(failure);
145 if (failure->kind == LOCK_CHK_FAIL_CAPACITY)
146 printf_unlocked("Capacity pool '%s': used %u / limit %u\n",
147 failure->capacity_pool ? failure->capacity_pool
148 : "<unknown>",
149 failure->capacity_used, failure->capacity_limit);
150 printf_unlocked("=========================================================="
151 "======================\n\n");
152}
153
154void lock_chk_report_failure(const struct lock_chk_failure *failure) {
155 uint64_t signature = lock_chk_failure_signature(failure);
156 char signature_text[24];
157 snprintf(signature_text, sizeof(signature_text), "%016lx", signature);
158
159 if (failure->kind == LOCK_CHK_FAIL_CAPACITY &&
160 !lock_chk_capacity_should_panic()) {
161 lock_chk_report_degradation(failure, signature, signature_text);
162 return;
163 }
164
165 lock_chk_print_failure(failure, signature_text);
166 lock_chk_emit_finding(failure, lock_chk_fail_kind_name(failure->kind),
167 signature_text);
168 nightmare_request_external_fail("lock_chk", signature, "%s", failure->msg);
169
170 crash_full(&(struct crash_context){
171 .source = CRASH_SOURCE_LOCK_CHK,
172 .formats = CRASH_FMT_DEFAULT,
173 .file = failure->site ? failure->site->file : __RELFILE__,
174 .line = failure->site ? failure->site->line : __LINE__,
175 .func = "lock_chk",
176 .msg = failure->msg,
177 .regs = NULL,
178 });
179}
180
181#endif /* DEBUG_LOCK_CHK */
182