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