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