The Pedigree Project 0.1
Debugger.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/Service.h"
22#include "pedigree/kernel/ServiceFeatures.h"
23#include "pedigree/kernel/ServiceManager.h"
24#include "pedigree/kernel/debugger/Debugger.h"
25#include "pedigree/kernel/debugger/DebuggerCommand.h"
26#include "pedigree/kernel/debugger/DebuggerIO.h"
27#include "pedigree/kernel/debugger/LocalIO.h"
28#include "pedigree/kernel/debugger/SerialIO.h"
29#include "pedigree/kernel/debugger/commands/AllocationCommand.h"
30#include "pedigree/kernel/debugger/commands/Backtracer.h"
31#include "pedigree/kernel/debugger/commands/BreakpointCommand.h"
32#include "pedigree/kernel/debugger/commands/CpuInfoCommand.h"
33#include "pedigree/kernel/debugger/commands/DevicesCommand.h"
34#include "pedigree/kernel/debugger/commands/DisassembleCommand.h"
35#include "pedigree/kernel/debugger/commands/DumpCommand.h"
36#include "pedigree/kernel/debugger/commands/HelpCommand.h"
37#include "pedigree/kernel/debugger/commands/IoCommand.h"
38#include "pedigree/kernel/debugger/commands/IrqsCommand.h"
39#include "pedigree/kernel/debugger/commands/LocksCommand.h"
40#include "pedigree/kernel/debugger/commands/LogViewer.h"
41#include "pedigree/kernel/debugger/commands/LookupCommand.h"
42#include "pedigree/kernel/debugger/commands/MappingCommand.h"
43#include "pedigree/kernel/debugger/commands/MemoryInspector.h"
44#include "pedigree/kernel/debugger/commands/ModulesCommand.h"
45#include "pedigree/kernel/debugger/commands/PanicCommand.h"
46#include "pedigree/kernel/debugger/commands/QuitCommand.h"
47#include "pedigree/kernel/debugger/commands/SlamCommand.h"
48#include "pedigree/kernel/debugger/commands/StepCommand.h"
49#include "pedigree/kernel/debugger/commands/SyscallTracerCommand.h"
50#include "pedigree/kernel/debugger/commands/ThreadsCommand.h"
51#include "pedigree/kernel/debugger/commands/TraceCommand.h"
52#include "pedigree/kernel/graphics/GraphicsService.h"
53#include "pedigree/kernel/machine/Display.h"
54#include "pedigree/kernel/machine/Keyboard.h"
55#include "pedigree/kernel/machine/Machine.h"
56#include "pedigree/kernel/machine/Serial.h"
57#include "pedigree/kernel/processor/InterruptManager.h"
58#include "pedigree/kernel/processor/Processor.h"
59#include "pedigree/kernel/processor/ProcessorInformation.h"
60#include "pedigree/kernel/processor/VirtualAddressSpace.h"
61#include "pedigree/kernel/processor/state.h"
62#include "pedigree/kernel/utilities/String.h"
63#include "pedigree/kernel/utilities/utility.h"
64
65class Thread;
66
68
72static int getCommandMatchingPrefix(char* prefix, DebuggerCommand** pCommands, size_t nCmds,
73 size_t start) {
74 for (size_t i = start; i < nCmds; i++) {
75 if (!StringCompareN(pCommands[i]->getString(), prefix, StringLength(prefix)))
76 return i;
77 }
78 return -1;
79}
80
84static bool matchesCommand(char* pStr, DebuggerCommand* pCommand) {
85 const size_t commandLength = StringLength(pCommand->getString());
86 if (StringCompareN(pCommand->getString(), pStr, commandLength)) {
87 return false;
88 }
89
90 const size_t inputLength = StringLength(pStr);
91 if (inputLength > commandLength && pStr[commandLength] != ' ' && pStr[commandLength] != '\t') {
92 return false;
93 }
94 if (inputLength == commandLength) {
95 // Existing commands use the command name to select no-argument
96 // behaviour, so preserve it while rejecting run-together prefixes.
97 return true;
98 }
99
100 MemoryCopy(pStr, pStr + commandLength + 1, inputLength - commandLength);
101 return true;
102}
103
104Debugger::Debugger() : m_pTempState(0), m_nIoType(DEBUGGER) {}
105
106Debugger::~Debugger() {}
107
109 if (!InterruptManager::instance().registerInterruptHandlerDebugger(
110 InterruptManager::instance().getBreakpointInterruptNumber(), this))
111 ERROR_NOLOCK("Debugger: breakpoint interrupt registration failed!");
112 if (!InterruptManager::instance().registerInterruptHandlerDebugger(
113 InterruptManager::instance().getDebugInterruptNumber(), this))
114 ERROR_NOLOCK("Debugger: debug interrupt registration failed!");
115}
116
118void Debugger::start(InterruptState& state, LargeStaticString& description) {
119 // Quiescing another CPU can fail while it holds an IRQ-disabling lock.
120 // Preserve the initiating exception before entering that barrier.
121 if (Machine::instance().getNumSerial()) {
122 Serial* serial = Machine::instance().getSerial(0);
123 if (serial) {
124 LargeStaticString entry("\nDebugger entry RIP=");
125 entry.append(state.getInstructionPointer(), 16);
126 entry += " CPU=";
127 entry.append(Processor::index(), 10);
128 entry += ": ";
129 entry += description;
130 entry += "\n";
131 serial->write_str(entry);
132 }
133 }
134#if MULTIPROCESSOR
135 const bool processorsQuiesced = Machine::instance().quiesceAllOtherProcessors();
136 if (!processorsQuiesced) {
137 ERROR_NOLOCK("Debugger: not all other processors quiesced");
138 }
139#endif
140
141 Log::LogEntry entry;
142 entry << Log::Notice << " << Flushing log content >>";
143 Log::instance().addEntry(entry);
144#if defined(VALGRIND) || defined(HAS_SANITIZERS)
146#endif
147 static String graphicsService("graphics");
148
149 // Drop out of whatever graphics mode we were in
151 ByteSet(&params, 0, sizeof(params));
152 params.wantTextMode = false;
153
154 ServiceFeatures* pFeatures = ServiceManager::instance().enumerateOperations(graphicsService);
155 Service* pService = ServiceManager::instance().getService(graphicsService);
156 bool bSuccess = false;
157 if (pFeatures && pFeatures->provides(ServiceFeatures::probe)) {
158 if (pService) {
159 bSuccess =
160 pService->serve(ServiceFeatures::probe, reinterpret_cast<void*>(&params), sizeof(params));
161 }
162 }
163
164 if (bSuccess && params.providerFound) {
165 // try and get back to text mode
166 if (params.providerResult.pDisplay) {
167 params.providerResult.pDisplay->setScreenMode(0);
168 }
169 }
170
171// We take a copy of the interrupt state here so that we can replace it with
172// another thread's interrupt state should we decide to switch threads. The
173// current thread, in case we decide to switch.
174#if THREADS
175 Thread* pThread = Processor::information().getCurrentThread();
176#endif
177
178 bool debugState = Machine::instance().getKeyboard()->getDebugState();
179 Machine::instance().getKeyboard()->setDebugState(true);
180
181 DebuggerIO* pInterfaces[2] = {0};
182
183#if !DONT_LOG_TO_SERIAL
184 static SerialIO serialIO(Machine::instance().getSerial(0));
185 serialIO.initialise();
186#endif
187
188 /*
189 * I/O implementations.
190 */
191 int nInterfaces = 0;
192 if (Machine::instance().getNumVga()) // Not all machines have "VGA", so handle that
193 {
194 static LocalIO localIO(Machine::instance().getVga(0), Machine::instance().getKeyboard());
195#if DONT_LOG_TO_SERIAL
196 pInterfaces[0] = &localIO;
197 nInterfaces = 1;
198#else
199 pInterfaces[0] = &localIO;
200 pInterfaces[1] = &serialIO;
201 nInterfaces = 2;
202#endif
203 }
204#if !DONT_LOG_TO_SERIAL
205 else {
206 pInterfaces[0] = &serialIO;
207 nInterfaces = 1;
208 }
209#endif
210
211 if (!nInterfaces) {
212 // Oops, system doesn't support any output mechanisms!
213 ERROR_NOLOCK(
214 "This machine/CPU combination doesn't support any output "
215 "methods for the debugger!");
216 }
217
218 // IO interface.
219 DebuggerIO* pIo = 0;
220 int nChosenInterface = -1;
221
222 // Commands.
223 static DisassembleCommand disassembler;
224 static LogViewer logViewer;
225 static Backtracer backtracer;
226 static QuitCommand quit;
227 static BreakpointCommand breakpoint;
228 static DumpCommand dump;
229 static StepCommand step;
230 static MemoryInspector memory;
231 static PanicCommand panic;
232 static CpuInfoCommand cpuInfo;
233 static IoCommand io;
234 static IrqsCommand irqs;
235 static DevicesCommand devices;
236 static SyscallTracerCommand syscallTracer;
237 static LookupCommand lookup;
238 static HelpCommand help;
239 static MappingCommand mapping;
240 static ModulesCommand modules;
241 static TraceCommand trace;
242
243#if THREADS
244 static ThreadsCommand threads;
245
246 threads.setPointers(&pThread, &state);
247#endif
248
249 DebuggerCommand* pCommands[] = {
250 &syscallTracer,
251 &disassembler,
252 &logViewer,
253 &backtracer,
254 &quit,
255 &breakpoint,
256 &dump,
257 &step,
258 &memory,
259 &trace,
260 &panic,
261 &cpuInfo,
262 &devices,
263#if THREADS
264 &threads,
265#endif
266 &io,
267 &irqs,
268 &g_AllocationCommand,
269 &g_SlamCommand,
270 &lookup,
271 &help,
272 &g_LocksCommand,
273 &mapping,
274 &modules,
275 };
276 const size_t nCommands = sizeof(pCommands) / sizeof(pCommands[0]);
277
278 // Are we going to jump directly into the tracer? In which case bypass
279 // device detection.
280 int n = trace.execTrace();
281 if (n == -1) {
282 // Write a "Press any key..." message to each device, then poll each
283 // device. The first one with data waiting becomes the active device,
284 // all others are locked out.
285 for (int i = 0; i < nInterfaces; i++) {
286 pInterfaces[i]->disableCli();
287 pInterfaces[i]->drawString("Press any key to enter the debugger...", 0, 0,
288 DebuggerIO::LightBlue, DebuggerIO::Black);
290 str += description;
291 pInterfaces[i]->drawString(str, 2, 0, DebuggerIO::LightBlue, DebuggerIO::Black);
292 }
293 // Poll each device.
294 while (pIo == 0) {
295 for (int i = 0; i < nInterfaces; i++) {
296 char c = pInterfaces[i]->getCharNonBlock();
297 if ((c >= 32 && static_cast<unsigned char>(c) <= 127) || c == '\n' || c == 0x08 ||
298 c == '\r' || c == 0x09) {
299 pIo = pInterfaces[i];
300 nChosenInterface = i;
301 break;
302 }
303 }
304 if (!pIo)
306 }
307 } else {
308 pIo = pInterfaces[n];
309 nChosenInterface = n;
310 }
311 pIo->readDimensions();
312
313 // Say sorry to the losers...
314 for (int i = 0; i < nInterfaces; i++)
315 if (pIo != pInterfaces[i])
316 pInterfaces[i]->drawString("Locked by another device.", 1, 0, DebuggerIO::LightRed,
317 DebuggerIO::Black);
318
319 pIo->setCliUpperLimit(1); // Give us room for a status bar on top.
320 pIo->setCliLowerLimit(1); // And a status bar on the bottom.
321 pIo->enableCli(); // Start CLI mode.
322
323 description += "\n";
324 pIo->writeCli(description, DebuggerIO::Yellow, DebuggerIO::Black);
325
326 description.clear();
327 description += "Kernel heap ends at ";
328 description.append(
329 reinterpret_cast<uintptr_t>(VirtualAddressSpace::getKernelAddressSpace().m_HeapEnd), 16);
330 description += "\n";
331 pIo->writeCli(description, DebuggerIO::Yellow, DebuggerIO::Black);
332
333 // Main CLI loop.
334 bool bKeepGoing = false;
335 do {
336 HugeStaticString command;
337 HugeStaticString output;
338 // Should we jump directly in to the tracer?
339 if (trace.execTrace() != -1) {
340 bKeepGoing = trace.execute(command, output, state, pIo);
341 continue;
342 } else
343 trace.setInterface(nChosenInterface);
344 // Clear the top and bottom status lines.
345 pIo->drawHorizontalLine(' ', 0, 0, pIo->getWidth() - 1, DebuggerIO::White, DebuggerIO::Green);
346 pIo->drawHorizontalLine(' ', pIo->getHeight() - 1, 0, pIo->getWidth() - 1, DebuggerIO::White,
347 DebuggerIO::Green);
348 // Write the correct text in the upper status line.
349 pIo->drawString("Pedigree debugger", 0, 0, DebuggerIO::White, DebuggerIO::Green);
350
351 bool matchedCommand = false;
352 DebuggerCommand* pAutoComplete = 0;
353 while (1) {
354 // Try and get a character from the CLI, passing in a buffer to
355 // populate and an autocomplete command for if the user presses TAB
356 // (if one is defined).
357 if (pIo->readCli(command, pAutoComplete))
358 break; // Command complete, try and parse it.
359
360 // The command wasn't complete - let's parse it and try and get an
361 // autocomplete string.
364 matchedCommand = false;
365 for (size_t i = 0; i < nCommands; i++) {
366 // TODO: This cast is completly wrong. As I said, don't touch
367 // (as in 'write') StaticString's
368 // internal string. The const is not there because I don't
369 // like you :-), it is there because directly writing is
370 // not garantueed to work (and it actually will break our
371 // code).
372 if (matchesCommand(const_cast<char*>(static_cast<const char*>(command)), pCommands[i])) {
373 str2 = static_cast<const char*>(pCommands[i]->getString());
374 str2 += ' ';
375 pCommands[i]->autocomplete(command, str);
376 matchedCommand = true;
377 break;
378 }
379 }
380
381 pAutoComplete = 0;
382 if (!matchedCommand) {
383 int i = -1;
384 while ((i = getCommandMatchingPrefix(const_cast<char*>(static_cast<const char*>(command)),
385 pCommands, nCommands, i + 1)) != -1) {
386 if (!pAutoComplete)
387 pAutoComplete = pCommands[i];
388 str += static_cast<const char*>(pCommands[i]->getString());
389 str += " ";
390 }
391 }
392
393 pIo->drawHorizontalLine(' ', pIo->getHeight() - 1, 0, pIo->getWidth() - 1, DebuggerIO::White,
394 DebuggerIO::Green);
395 pIo->drawString(str2, pIo->getHeight() - 1, 0, DebuggerIO::Yellow, DebuggerIO::Green);
396 pIo->drawString(str, pIo->getHeight() - 1, str2.length(), DebuggerIO::White,
397 DebuggerIO::Green);
398 }
399
400 // A command was entered.
401 bool bValidCommand = false;
402 for (size_t i = 0; i < nCommands; i++) {
403 if (matchesCommand(const_cast<char*>(static_cast<const char*>(command)), pCommands[i])) {
404 bKeepGoing = pCommands[i]->execute(command, output, state, pIo);
405 pIo->writeCli(output, DebuggerIO::LightGrey, DebuggerIO::Black);
406 bValidCommand = true;
407 }
408 }
409
410 if (!bValidCommand) {
411 pIo->writeCli("Unrecognised command.\n", DebuggerIO::LightGrey, DebuggerIO::Black);
412 bKeepGoing = true;
413 }
414
415 } while (bKeepGoing);
416 if (Machine::instance().getNumVga())
417 pInterfaces[0]->destroy(); // Causes rememberMode to be called twice.
418#if !DONT_LOG_TO_SERIAL
419 serialIO.destroy();
420#endif
421
422 Machine::instance().getKeyboard()->setDebugState(debugState);
423#if MULTIPROCESSOR
424 if (processorsQuiesced && !Machine::instance().resumeAllOtherProcessors()) {
425 ERROR_NOLOCK("Debugger: not all quiesced processors resumed");
426 }
427#endif
428}
429
430void Debugger::interrupt(size_t interruptNumber, InterruptState& state) {
431 LargeStaticString description;
432 // We switch here on the interrupt number, and dispatch accordingly.
433 if (interruptNumber == InterruptManager::instance().getBreakpointInterruptNumber()) {
434 // Here we check to see if the breakpoint was caused by an assertion, or
435 // a fatal error.
436 if (state.getRegister(0) == ASSERT_FAILED_SENTINEL) {
437 // As it's an assert or fatal, we assume state.getRegister(1) is a
438 // pointer to a descriptive string.
439 const char* pDescription = reinterpret_cast<const char*>(state.getRegister(1));
440 description += pDescription;
441 } else {
442 description += "Breakpoint exception.";
443 }
444 start(state, description);
445 } else if (interruptNumber == InterruptManager::instance().getDebugInterruptNumber()) {
446 Processor::setSingleStep(false, state);
447 description = "Debug/trap exception";
448 start(state, description);
449 }
450}
virtual const NormalStaticString getString()=0
virtual bool execute(const HugeStaticString &input, HugeStaticString &output, InterruptState &state, DebuggerIO *screen)=0
virtual void autocomplete(const HugeStaticString &input, HugeStaticString &output)=0
virtual void enableCli()=0
virtual bool readCli(HugeStaticString &str, DebuggerCommand *pAutoComplete)
Definition DebuggerIO.cc:25
virtual void setCliUpperLimit(size_t nlines)=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 writeCli(const char *str, Colour foreColour, Colour backColour)
virtual void drawHorizontalLine(char c, size_t row, size_t colStart, size_t colEnd, Colour foreColour, Colour backColour)=0
void start(InterruptState &state, LargeStaticString &description)
Definition Debugger.cc:118
void initialise()
Definition Debugger.cc:108
virtual void interrupt(size_t interruptNumber, InterruptState &state)
Definition Debugger.cc:430
static Debugger m_Instance
Definition Debugger.h:86
virtual bool setScreenMode(ScreenMode sm)
Definition Display.cc:90
static EXPORTED_PUBLIC InterruptManager & instance()
virtual void setDebugState(bool enableDebugState)=0
EXPORTED_PUBLIC void addEntry(const LogEntry &entry, bool lock=true)
Definition Log.cc:602
static EXPORTED_PUBLIC Log & instance()
Definition Log.cc:117
virtual MUST_USE_RESULT bool quiesceAllOtherProcessors()
Definition Machine.cc:127
virtual Keyboard * getKeyboard()=0
virtual Serial * getSerial(size_t n)=0
virtual size_t getNumVga()=0
static void halt()
static ProcessorInformation & information()
static void pause()
static void setSingleStep(bool bEnable, InterruptState &state)
static size_t index()
virtual bool provides(Type service)
Service * getService(const String &serviceName)
ServiceFeatures * enumerateOperations(const String &serviceName)
virtual bool serve(ServiceFeatures::Type type, void *pData, size_t dataLen)=0
SchedulerState & state()
Definition Thread.cc:806
bool start()
Definition Thread.cc:725
void setPointers(Thread **ppThread, InterruptState *pState)
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:117