1 // Copyright 2018 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6
7 #include <arch/ops.h>
8 #include <debug.h>
9 #include <kernel/event.h>
10 #include <kernel/percpu.h>
11 #include <kernel/thread.h>
12 #include <lk/init.h>
13 #include <vm/vm.h>
14
15 #include <lib/console.h>
16 #include <lib/version.h>
17
18 #include <inttypes.h>
19 #include <new>
20 #include <string.h>
21
22 #include <fbl/atomic.h>
23 #include <lockdep/lockdep.h>
24
25 // Always assert to catch changes when lockdep is not enabled.
26 static_assert(sizeof(lockdep_state_t) == sizeof(lockdep::ThreadLockState),
27 "lockdep_state_t and lockdep::ThreadLockState out of sync!");
28
29 #if WITH_LOCK_DEP
30
31 namespace {
32
33 // Event to wake up the loop detector thread when a new edge is added to the
34 // lock dependency graph.
35 event_t graph_edge_event =
36 EVENT_INITIAL_VALUE(graph_edge_event, false, EVENT_FLAG_AUTOUNSIGNAL);
37
38 // Loop detection thread. Traverses the lock dependency graph to find circular
39 // lock dependencies.
LockDepThread(void *)40 int LockDepThread(void* /*arg*/) {
41 while (true) {
42 __UNUSED zx_status_t error = event_wait(&graph_edge_event);
43
44 // Add some hysteresis to avoid re-triggering the loop detector on
45 // close successive updates to the graph and to give the inline
46 // validation reports a chance to print out first.
47 thread_sleep_relative(ZX_SEC(2));
48
49 lockdep::LoopDetectionPass();
50 }
51 return 0;
52 }
53
LockDepInit(unsigned)54 void LockDepInit(unsigned /*level*/) {
55 thread_t* t = thread_create("lockdep", &LockDepThread, NULL, LOW_PRIORITY);
56 thread_detach_and_resume(t);
57 }
58
59 // Dumps the state of the lock dependency graph.
DumpLockClassState()60 void DumpLockClassState() {
61 printf("Lock class states:\n");
62 for (auto& state : lockdep::LockClassState::Iter()) {
63 printf(" %s {\n", state.name());
64 for (lockdep::LockClassId id : state.dependency_set()) {
65 printf(" %s\n", lockdep::LockClassState::GetName(id));
66 }
67 printf(" }\n");
68 }
69 printf("\nConnected sets:\n");
70 for (auto& state : lockdep::LockClassState::Iter()) {
71 // Only handle root nodes in the outer loop. The nested loop will pick
72 // up all of the child nodes under each parent node.
73 if (state.connected_set() == &state) {
74 printf("{\n");
75 for (auto& other_state : lockdep::LockClassState::Iter()) {
76 if (other_state.connected_set() == &state)
77 printf(" %s\n", other_state.name());
78 }
79 printf("}\n");
80 }
81 }
82 }
83
84 // Top-level lockdep command.
CommandLockDep(int argc,const cmd_args * argv,uint32_t flags)85 int CommandLockDep(int argc, const cmd_args* argv, uint32_t flags) {
86 if (argc < 2) {
87 printf("Not enough arguments:\n");
88 usage:
89 printf("%s dump : dump lock classes\n", argv[0].str);
90 printf("%s loop : trigger loop detection pass\n", argv[0].str);
91 return -1;
92 }
93
94 if (strcmp(argv[1].str, "dump") == 0) {
95 DumpLockClassState();
96 } else if (strcmp(argv[1].str, "loop") == 0) {
97 printf("Triggering loop detection pass:\n");
98 lockdep::SystemTriggerLoopDetection();
99 } else {
100 printf("Unrecognized subcommand: '%s'\n", argv[1].str);
101 goto usage;
102 }
103
104 return 0;
105 }
106
107 // Utility to cast from lockdep_state_t* to ThreadLockState*.
ToThreadLockState(lockdep_state_t * state)108 inline lockdep::ThreadLockState* ToThreadLockState(lockdep_state_t* state) {
109 return reinterpret_cast<lockdep::ThreadLockState*>(state);
110 }
111
112 } // anonymous namespace
113
114 STATIC_COMMAND_START
115 STATIC_COMMAND("lockdep", "kernel lock diagnostics", &CommandLockDep)
116 STATIC_COMMAND_END(lockdep);
117
118 LK_INIT_HOOK(lockdep, LockDepInit, LK_INIT_LEVEL_THREADING);
119
120 namespace lockdep {
121
122 // Prints a kernel oops when a normal lock order violation is detected.
SystemLockValidationError(AcquiredLockEntry * bad_entry,AcquiredLockEntry * conflicting_entry,ThreadLockState * state,void * caller_address,void * caller_frame,LockResult result)123 void SystemLockValidationError(AcquiredLockEntry* bad_entry,
124 AcquiredLockEntry* conflicting_entry,
125 ThreadLockState* state,
126 void* caller_address,
127 void* caller_frame,
128 LockResult result) {
129 thread_t* const current_thread = get_current_thread();
130
131 char owner_name[THREAD_NAME_LENGTH];
132 thread_owner_name(current_thread, owner_name);
133
134 const uint64_t user_pid = current_thread->user_pid;
135 const uint64_t user_tid = current_thread->user_tid;
136
137 printf("\nZIRCON KERNEL PANIC\n");
138 printf("Lock validation failed for thread %p pid %" PRIu64 " tid %" PRIu64 " (%s:%s):\n",
139 current_thread, user_pid, user_tid, owner_name, current_thread->name);
140 printf("Reason: %s\n", ToString(result));
141 printf("Bad lock: name=%s order=%" PRIu64 "\n",
142 LockClassState::GetName(bad_entry->id()),
143 bad_entry->order());
144 printf("Conflict: name=%s order=%" PRIu64 "\n",
145 LockClassState::GetName(conflicting_entry->id()),
146 conflicting_entry->order());
147 printf("caller=%p frame=%p\n", caller_address, caller_frame);
148 printf("BUILDID %s\n", version.buildid);
149
150 // Log the ELF build ID in the format the symbolizer scripts understand.
151 if (version.elf_build_id[0] != '\0') {
152 printf("dso: id=%s base=%#lx name=zircon.elf\n",
153 version.elf_build_id, (uintptr_t)__code_start);
154 }
155
156 thread_print_current_backtrace_at_frame(caller_frame);
157 printf("\n");
158 }
159
160 // Issues a kernel panic when a fatal lock order violation is detected.
SystemLockValidationFatal(AcquiredLockEntry * lock_entry,ThreadLockState * state,void * caller_address,void * caller_frame,LockResult result)161 void SystemLockValidationFatal(AcquiredLockEntry* lock_entry,
162 ThreadLockState* state,
163 void* caller_address,
164 void* caller_frame,
165 LockResult result) {
166 _panic(caller_address, caller_frame,
167 "Fatal lock violation detected! name=%s reason=%s\n",
168 LockClassState::GetName(lock_entry->id()), ToString(result));
169 }
170
171 // Prints a kernel oops when a circular lock dependency is detected.
SystemCircularLockDependencyDetected(LockClassState * connected_set_root)172 void SystemCircularLockDependencyDetected(LockClassState* connected_set_root) {
173 printf("\nZIRCON KERNEL OOPS\n");
174 printf("Circular lock dependency detected:\n");
175
176 for (auto& node : lockdep::LockClassState::Iter()) {
177 if (node.connected_set() == connected_set_root)
178 printf(" %s\n", node.name());
179 }
180
181 printf("\n");
182 }
183
184 // Returns a pointer to the ThreadLockState instance for the current thread when
185 // thread context or for the current CPU when in irq context.
SystemGetThreadLockState()186 ThreadLockState* SystemGetThreadLockState() {
187 ThreadLockState* state;
188
189 if (arch_blocking_disallowed())
190 state = ToThreadLockState(&get_local_percpu()->lock_state);
191 else
192 state = ToThreadLockState(&get_current_thread()->lock_state);
193
194 return state;
195 }
196
197 // Initializes an instance of ThreadLockState.
SystemInitThreadLockState(ThreadLockState * state)198 void SystemInitThreadLockState(ThreadLockState* state) {
199 new (state) ThreadLockState();
200 }
201
202 // Wakes up the loop detector thread to re-evaluate the dependency graph.
SystemTriggerLoopDetection()203 void SystemTriggerLoopDetection() {
204 event_signal(&graph_edge_event, /*reschedule=*/false);
205 }
206
207 } // namespace lockdep
208
209 #endif
210