The Pedigree Project 0.1
LocksCommand.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/Log.h"
21#include "pedigree/kernel/Spinlock.h"
22#include "pedigree/kernel/debugger/commands/LocksCommand.h"
23#include "pedigree/kernel/linker/KernelElf.h"
24#include "pedigree/kernel/processor/Processor.h"
25#include "pedigree/kernel/utilities/demangle.h"
26
27LocksCommand g_LocksCommand;
28#ifndef TESTSUITE
29extern Spinlock g_MallocLock;
30#endif
31
32// This is global because we need to rely on it before the constructor is
33// called.
34static bool g_bReady = false;
35
36#define ERROR_OR_FATAL(x) \
37 do { \
38 if (m_bFatal) \
39 FATAL_NOLOCK(x); \
40 else \
41 ERROR_NOLOCK(x); \
42 } while (0)
43
46 m_pDescriptors(),
47 m_bAcquiring(false),
48 m_LockIndex(0),
49 m_bFatal(true),
50 m_SelectedLine(0) {
51 for (size_t i = 0; i < LOCKS_COMMAND_NUM_CPU; ++i) {
52 m_NextPosition[i] = 0;
53 }
54}
55
57
59
61 InterruptState& state, DebuggerIO* pScreen) {
62#if !TRACK_LOCKS
63 output += "Sorry, this kernel was not built with TRACK_LOCKS enabled.";
64 return true;
65#endif
66
67 if (!g_bReady) {
68 output += "Lock tracking has not yet been enabled.";
69 return true;
70 }
71
72 // Let's enter 'raw' screen mode.
73 pScreen->disableCli();
74
75 // Prepare Scrollable interface.
76 move(0, 1);
77 resize(pScreen->getWidth(), pScreen->getHeight() - 2);
78 setScrollKeys('j', 'k');
79
80 pScreen->drawHorizontalLine(' ', 0, 0, pScreen->getWidth() - 1, DebuggerIO::White,
81 DebuggerIO::Green);
82 pScreen->drawHorizontalLine(' ', pScreen->getHeight() - 1, 0, pScreen->getWidth() - 1,
83 DebuggerIO::White, DebuggerIO::Green);
84 pScreen->drawString("Pedigree debugger - Lock tracker", 0, 0, DebuggerIO::White,
85 DebuggerIO::Green);
86
87 pScreen->drawString("backspace: Page up. space: Page down. q: Quit.", pScreen->getHeight() - 1, 0,
88 DebuggerIO::White, DebuggerIO::Green);
89 pScreen->drawString("backspace", pScreen->getHeight() - 1, 0, DebuggerIO::Yellow,
90 DebuggerIO::Green);
91 pScreen->drawString("space", pScreen->getHeight() - 1, 20, DebuggerIO::Yellow, DebuggerIO::Green);
92 pScreen->drawString("q", pScreen->getHeight() - 1, 38, DebuggerIO::Yellow, DebuggerIO::Green);
93
94 // Main I/O loop.
95 bool bStop = false;
96 bool bReturn = true;
97 while (!bStop) {
98 refresh(pScreen);
99
100 char in = 0;
101 while (!(in = pScreen->getChar()))
102 ;
103
104 switch (in) {
105 case 'j':
106 scroll(-1);
107 if (static_cast<ssize_t>(m_SelectedLine) - 1 >= 0)
108 --m_SelectedLine;
109 break;
110
111 case 'k':
112 scroll(1);
113 if (m_SelectedLine + 1 < getLineCount())
114 ++m_SelectedLine;
115 break;
116
117 case ' ':
118 scroll(5);
119 if (m_SelectedLine + 5 < getLineCount())
120 m_SelectedLine += 5;
121 else
122 m_SelectedLine = getLineCount() - 1;
123 break;
124
125 case 0x08: // backspace
126 scroll(-5);
127 if (static_cast<ssize_t>(m_SelectedLine) - 5 >= 0)
128 m_SelectedLine -= 5;
129 else
130 m_SelectedLine = 0;
131 break;
132
133 case 'q':
134 bStop = true;
135 }
136 }
137
138 // HACK:: Serial connections will fill the screen with the last background
139 // colour used.
140 // Here we write a space with black background so the CLI screen
141 // doesn't get filled by some random colour!
142 pScreen->drawString(" ", 1, 0, DebuggerIO::White, DebuggerIO::Black);
143 pScreen->enableCli();
144 return bReturn;
145}
146
147const char* LocksCommand::getLine1(size_t index, DebuggerIO::Colour& colour,
148 DebuggerIO::Colour& bgColour) {
149 static NormalStaticString Line;
150 Line.clear();
151
152 size_t nLock = 0;
153 size_t nDepth = 0;
154 size_t nCpu = 0;
155 LockDescriptor* pD = 0;
156 for (nCpu = 0; nCpu < LOCKS_COMMAND_NUM_CPU; ++nCpu) {
157 if (nLock++ == index) {
158 break;
159 }
160
161 if (!m_NextPosition[nCpu]) {
162 continue;
163 }
164
165 nDepth = 0;
166 for (size_t j = 0; j < MAX_DESCRIPTORS; ++j) {
167 pD = &m_pDescriptors[nCpu][j];
168 if (pD->state == Inactive) {
169 pD = 0;
170 break;
171 } else if (nLock == index) {
172 break;
173 }
174
175 ++nDepth;
176 ++nLock;
177 }
178
179 if (pD) {
180 break;
181 }
182 }
183
184 if ((nLock - 1) > index) {
185 return Line;
186 }
187
188 if (!pD) {
189 Line += "CPU";
190 Line.append(nCpu);
191 Line += " (";
192 Line.append(m_NextPosition[nCpu]);
193 Line += " locks):";
194 } else {
195 Line += " | ";
196 }
197
198 colour = DebuggerIO::White;
199 if (index == m_SelectedLine)
200 bgColour = DebuggerIO::Blue;
201 else
202 bgColour = DebuggerIO::Black;
203
204 return Line;
205}
206
207const char* LocksCommand::getLine2(size_t index, size_t& colOffset, DebuggerIO::Colour& colour,
208 DebuggerIO::Colour& bgColour) {
209 static HugeStaticString Line;
210 Line.clear();
211
212 size_t nLock = 0;
213 size_t nDepth = 0;
214 size_t nCpu = 0;
215 LockDescriptor* pD = 0;
216 bool doBacktrace = false;
217 for (nCpu = 0; nCpu < LOCKS_COMMAND_NUM_CPU; ++nCpu) {
218 if (!m_NextPosition[nCpu]) {
219 continue;
220 }
221
222 if (nLock++ == index) {
223 break;
224 }
225
226 nDepth = 0;
227 for (size_t j = 0; j < MAX_DESCRIPTORS; ++j) {
228 pD = &m_pDescriptors[nCpu][j];
229 if (pD->state == Inactive) {
230 pD = 0;
231 break;
232 }
233 if (nLock == index) {
234 break;
235 }
236
237 ++nDepth;
238 ++nLock;
239 }
240
241 if (pD) {
242 break;
243 }
244 }
245
246 if (!pD) {
247 return Line;
248 }
249
250 colOffset = nDepth + 3;
251
252 {
253 Line.append(reinterpret_cast<uintptr_t>(pD->pLock), 16);
254 Line += " state=";
255 Line += stateName(pD->state);
256 Line += " caller=";
257 Line.append(pD->pLock->acquisitionAddress(), 16);
258
259#ifndef TESTSUITE
260 uintptr_t symStart = 0;
261 const char* pSym =
262 KernelElf::instance().globalLookupSymbol(pD->pLock->acquisitionAddress(), &symStart);
263 if (pSym) {
264 LargeStaticString sym(pSym);
265
266 Line += " ";
267
269 demangle(sym, &symbol);
270 Line += static_cast<const char*>(symbol.name);
271 }
272#endif
273 }
274
275 colour = DebuggerIO::White;
276 if (index == m_SelectedLine)
277 bgColour = DebuggerIO::Blue;
278 else
279 bgColour = DebuggerIO::Black;
280
281 return Line;
282}
283
284size_t LocksCommand::getLineCount() {
285 size_t numLocks = 0;
286 for (size_t i = 0; i < LOCKS_COMMAND_NUM_CPU; ++i) {
287 size_t nextPos = m_NextPosition[i];
288 if (nextPos) {
289 // For the CPU line to appear.
290 ++numLocks;
291 }
292
293 numLocks += nextPos;
294 }
295
296 return numLocks;
297}
298
299void LocksCommand::setReady() {
300 g_bReady = true;
301}
302
304 m_bFatal = true;
305}
306
307void LocksCommand::clearFatal() {
308 m_bFatal = false;
309}
310
311bool LocksCommand::lockAttempted(const Spinlock* pLock, size_t nCpu, bool intState) {
312 if (!g_bReady)
313 return true;
314 if (pLock->m_bAvoidTracking)
315 return true;
316 if (nCpu == ~0U)
317 nCpu = Processor::index();
318
319 size_t pos = (m_NextPosition[nCpu] += 1) - 1;
320 if (pos > MAX_DESCRIPTORS) {
321 ERROR_OR_FATAL("Spinlock " << Hex << pLock << " ran out of room for locks [" << Dec << pos
322 << "].");
323 return false;
324 }
325
326 if (pos && intState) {
327 // We're more than one lock deep, but interrupts are enabled!
328 ERROR_OR_FATAL("Spinlock " << Hex << pLock << " attempted at level " << Dec << pos << Hex
329 << " with interrupts enabled on CPU" << Dec << nCpu << ".");
330 return false;
331 }
332
333 LockDescriptor* pD = &m_pDescriptors[nCpu][pos];
334
335 if (pD->state != Inactive) {
336 ERROR_OR_FATAL("LocksCommand tracking state is corrupt.");
337 return false;
338 }
339
340 pD->pLock = pLock;
341 pD->state = Attempted;
342
343#ifndef TESTSUITE
344#endif
345
346 return true;
347}
348
349bool LocksCommand::lockAcquired(const Spinlock* pLock, size_t nCpu, bool intState) {
350 if (!g_bReady)
351 return true;
352 if (pLock->m_bAvoidTracking)
353 return true;
354 if (nCpu == ~0U)
355 nCpu = Processor::index();
356
357 size_t back = m_NextPosition[nCpu] - 1;
358 if (back > MAX_DESCRIPTORS) {
359 ERROR_OR_FATAL("Spinlock " << Hex << pLock << " acquired unexpectedly (no tracked locks).");
360 return false;
361 }
362
363 if (back && intState) {
364 // We're more than one lock deep, but interrupts are enabled!
365 ERROR_OR_FATAL("Spinlock " << Hex << pLock << " acquired at level " << Dec << back << Hex
366 << " with interrupts enabled on CPU" << Dec << nCpu << ".");
367 return false;
368 }
369
370 LockDescriptor* pD = &m_pDescriptors[nCpu][back];
371
372 if (pD->state != Attempted || pD->pLock != pLock) {
373 ERROR_OR_FATAL("Spinlock " << Hex << pLock << " acquired unexpectedly.");
374 return false;
375 }
376
377 pD->state = Acquired;
378
379 return true;
380}
381
382bool LocksCommand::lockReleased(const Spinlock* pLock, size_t nCpu) {
383 if (!g_bReady)
384 return true;
385 if (pLock->m_bAvoidTracking)
386 return true;
387 if (nCpu == ~0U)
388 nCpu = Processor::index();
389
390 size_t back = m_NextPosition[nCpu] - 1;
391
392 LockDescriptor* pD = &m_pDescriptors[nCpu][back];
393
394 if (pD->state != Acquired || pD->pLock != pLock) {
395 // Maybe we need to unwind another CPU.
397 bool ok = false;
398 for (size_t i = 0; i < LOCKS_COMMAND_NUM_CPU; ++i) {
399 if (i == nCpu)
400 continue;
401
402 back = m_NextPosition[i] - 1;
403 if (back < MAX_DESCRIPTORS) {
404 LockDescriptor* pCheckD = &m_pDescriptors[i][back];
405 if (pCheckD->state == Acquired && pCheckD->pLock == pLock) {
406 nCpu = i;
407 ok = true;
408 pD = pCheckD;
409 break;
410 }
411 }
412 }
413
414 if (!ok) {
415 ERROR_OR_FATAL("Spinlock " << Hex << pLock << " released out-of-order [expected lock "
416 << (pD ? pD->pLock : 0) << (pD ? "" : " (no lock)") << ", state "
417 << (pD ? stateName(pD->state) : "(no state)") << "].");
418 return false;
419 }
420 }
421
422 pD->pLock = 0;
423 pD->state = Inactive;
424
425 m_NextPosition[nCpu] -= 1;
426
427 return true;
428}
429
431 if (!g_bReady)
432 return true;
433 if (nCpu == ~0U)
434 nCpu = Processor::index();
435
436 size_t pos = m_NextPosition[nCpu];
437 if (pos) {
438 const LockDescriptor& retained = m_pDescriptors[nCpu][pos - 1];
439 ERROR_NOLOCK("Reschedule retained lock "
440 << Hex << retained.pLock << " acquired at "
441 << (retained.pLock ? retained.pLock->acquisitionAddress() : 0)
442 << ", schedule caller "
443 << reinterpret_cast<uintptr_t>(__builtin_return_address(0)));
444 ERROR_OR_FATAL("Rescheduling CPU" << nCpu << " is not allowed, as there are still " << pos
445 << " acquired locks.");
446 return false;
447 }
448
449 return true;
450}
451
452bool LocksCommand::checkState(const Spinlock* pLock, size_t nCpu) {
453 if (!g_bReady)
454 return true;
455 if (pLock->m_bAvoidTracking)
456 return true;
457 if (nCpu == ~0U)
458 nCpu = Processor::index();
459
460 bool bResult = true;
461
462 // Enter critical section for all cores.
463 while (!m_bAcquiring.compareAndSwap(false, true))
465
466 // Check state of our lock against all other CPUs.
467 for (size_t i = 0; i < LOCKS_COMMAND_NUM_CPU; ++i) {
468 if (i == nCpu)
469 continue;
470
471 bool foundLock = false;
472 LockDescriptor* pD = 0;
473 for (size_t j = 0; j < m_NextPosition[i]; ++j) {
474 pD = &m_pDescriptors[i][j];
475 if (pD->state == Inactive) {
476 pD = 0;
477 break;
478 }
479
480 if (pD->pLock == pLock && pD->state == Acquired) {
481 foundLock = true;
482 }
483 }
484
485 // If the most recent lock they tried is ours, we're OK.
486 if (!foundLock || !pD || pD->pLock == pLock) {
487 continue;
488 }
489
490 if (pD->state != Attempted) {
491 continue;
492 }
493
494 // Okay, we have an attempted lock, which we could hold.
495 for (size_t j = 0; j < m_NextPosition[nCpu]; ++j) {
496 LockDescriptor* pMyD = &m_pDescriptors[nCpu][j];
497 if (pMyD->state == Inactive) {
498 break;
499 }
500
501 if (pMyD->pLock == pD->pLock && pMyD->state == Acquired) {
502 // We hold their attempted lock. We're waiting on them.
503 // Deadlock.
504 ERROR_OR_FATAL("Detected lock dependency inversion (deadlock) between "
505 << Hex << pLock << " and " << pD->pLock << "!");
506 bResult = false;
507 break;
508 }
509 }
510 }
511
512 // Done with critical section.
513 m_bAcquiring = false;
514
515 return bResult;
516}
virtual void enableCli()=0
virtual void drawString(const char *str, size_t row, size_t col, Colour foreColour, Colour backColour)=0
virtual size_t getWidth()=0
virtual void drawHorizontalLine(char c, size_t row, size_t colStart, size_t colEnd, Colour foreColour, Colour backColour)=0
virtual char getChar()=0
static KernelElf & instance()
Definition KernelElf.h:135
uintptr_t globalLookupSymbol(const char *pName)
bool checkState(const Spinlock *pLock, size_t nCpu=~0U)
bool checkSchedule(size_t nCpu=~0U)
virtual ~LocksCommand()
Atomic< uint8_t > m_NextPosition[LOCKS_COMMAND_NUM_CPU]
void autocomplete(const HugeStaticString &input, HugeStaticString &output)
@ Inactive
This entry is no longer active.
@ Attempted
The lock is about to be attempted.
@ Acquired
The lock is acquired.
bool lockReleased(const Spinlock *pLock, size_t nCpu=~0U)
bool execute(const HugeStaticString &input, HugeStaticString &output, InterruptState &state, DebuggerIO *screen)
static void pause()
static size_t index()
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124