The Pedigree Project  0.1
TraceCommand.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/commands/TraceCommand.h"
21 #include "pedigree/kernel/debugger/DebuggerIO.h"
22 #include "pedigree/kernel/linker/KernelElf.h"
23 #include "pedigree/kernel/processor/Disassembler.h"
24 #include "pedigree/kernel/processor/Processor.h"
25 #include "pedigree/kernel/processor/state.h"
26 #include "pedigree/kernel/utilities/demangle.h"
27 
28 TraceCommand::TraceCommand() : DebuggerCommand(), m_nExec(-1), m_nInterface(-1)
29 {
30 }
31 
33 {
34 }
35 
37  const HugeStaticString &input, HugeStaticString &output)
38 {
39 }
40 
41 void TraceCommand::setInterface(int nInterface)
42 {
43  m_nInterface = nInterface;
44 }
45 
47  const HugeStaticString &input, HugeStaticString &output,
48  InterruptState &state, DebuggerIO *pScreen)
49 {
50  m_nExec = -1;
51  // Let's enter 'raw' screen mode.
52  pScreen->disableCli();
53 
54  // Work out where the split will be between the disassembler, reg dump and
55  // stacktrace. Give the reg dump 25 columns, hardcoded.
56  int nCols = pScreen->getWidth() - 25;
57  // Give the disassembler 3/4 of the available lines.
58  int nLines = ((pScreen->getHeight() - 2) * 3) / 4;
59 
60  static Disassembly disassembly(state);
61  disassembly.move(0, 1);
62  disassembly.resize(nCols, nLines);
63  disassembly.setScrollKeys('o', 'p');
64 
65  static Registers registers(state);
66  registers.move(nCols + 1, 1);
67  registers.resize(24, nLines);
68  registers.setScrollKeys('j', 'k');
69 
70  static Stacktrace stacktrace(state);
71  stacktrace.move(0, nLines + 2);
72  stacktrace.resize(pScreen->getWidth(), pScreen->getHeight() - nLines - 3);
73  stacktrace.setScrollKeys('n', 'm');
74 
75  size_t nInstruction = 0;
76 
77  // find the first instruction for the function that we're in right now
78  uintptr_t nIp = state.getInstructionPointer();
79  uintptr_t nSymStart = 0;
80  KernelElf::instance().globalLookupSymbol(nIp, &nSymStart);
81 
82  static Disassembler disassembler;
83 #ifdef BITS_64
84  disassembler.setMode(64);
85 #endif
86  disassembler.setLocation(nSymStart);
87 
88  uintptr_t nLocation;
89  LargeStaticString text;
90  while (nInstruction < disassembly.getLineCount())
91  {
92  nLocation = disassembler.getLocation();
93  text.clear();
94  disassembler.disassemble(text);
95  nInstruction++;
96  if (nLocation == state.getInstructionPointer())
97  break;
98  }
99 
100  disassembly.centreOn(nInstruction);
101 
102  pScreen->disableRefreshes();
103  drawBackground(nCols, nLines, pScreen);
104  registers.refresh(pScreen);
105  disassembly.refresh(pScreen);
106  stacktrace.refresh(pScreen);
107  pScreen->enableRefreshes();
108 
109  // Here we enter our main runloop.
110  bool bContinue = true;
111  while (bContinue)
112  {
113  char c;
114  while ((c = pScreen->getChar()) == 0)
115  ;
116  if (c == 'q')
117  break;
118  else if (c == 's')
119  {
120  m_nExec = m_nInterface;
121  Processor::setSingleStep(true, state);
122  return false;
123  }
124  else if (c == 'o')
125  {
126  disassembly.scroll(-1);
127  disassembly.refresh(pScreen);
128  }
129  else if (c == 'p')
130  {
131  disassembly.scroll(1);
132  disassembly.refresh(pScreen);
133  }
134  else if (c == 'j')
135  {
136  registers.scroll(-1);
137  registers.refresh(pScreen);
138  }
139  else if (c == 'k')
140  {
141  registers.scroll(1);
142  registers.refresh(pScreen);
143  }
144  else if (c == 'n')
145  {
146  stacktrace.scroll(-1);
147  stacktrace.refresh(pScreen);
148  }
149  else if (c == 'm')
150  {
151  stacktrace.scroll(1);
152  stacktrace.refresh(pScreen);
153  }
154  }
155 
156  // Let's enter CLI screen mode again.
157  pScreen->enableCli();
158  return true; // Return control to the debugger.
159 }
160 
161 void TraceCommand::drawBackground(
162  size_t nCols, size_t nLines, DebuggerIO *pScreen)
163 {
164  // Destroy all text.
165  pScreen->cls();
166 
167  // Clear the top and bottom status lines.
168  pScreen->drawHorizontalLine(
169  ' ', 0, 0, pScreen->getWidth() - 1, DebuggerIO::White,
170  DebuggerIO::Green);
171  pScreen->drawHorizontalLine(
172  ' ', pScreen->getHeight() - 1, 0, pScreen->getWidth() - 1,
173  DebuggerIO::White, DebuggerIO::Green);
174  // Write the correct text in the upper status line.
175  pScreen->drawString(
176  "Pedigree debugger - Execution Tracer", 0, 0, DebuggerIO::White,
177  DebuggerIO::Green);
178  // Write some helper text in the lower status line.
179  pScreen->drawString(
180  "s: Step. c: Continue. q: Quit. ?: Current instruction. ?: Breakpoint.",
181  pScreen->getHeight() - 1, 0, DebuggerIO::White, DebuggerIO::Green);
182  pScreen->drawString(
183  "s", pScreen->getHeight() - 1, 0, DebuggerIO::Yellow,
184  DebuggerIO::Green);
185  pScreen->drawString(
186  "c", pScreen->getHeight() - 1, 9, DebuggerIO::Yellow,
187  DebuggerIO::Green);
188  pScreen->drawString(
189  "q", pScreen->getHeight() - 1, 22, DebuggerIO::Yellow,
190  DebuggerIO::Green);
191  pScreen->drawString(
192  " ", pScreen->getHeight() - 1, 31, DebuggerIO::Yellow,
193  DebuggerIO::Blue);
194  pScreen->drawString(
195  " ", pScreen->getHeight() - 1, 55, DebuggerIO::Yellow, DebuggerIO::Red);
196 
197  pScreen->drawHorizontalLine(
198  '-', nLines + 1, 0, pScreen->getWidth(), DebuggerIO::DarkGrey,
199  DebuggerIO::Black);
200  pScreen->drawVerticalLine(
201  '|', nCols, 1, nLines, DebuggerIO::DarkGrey, DebuggerIO::Black);
202 }
203 
204 TraceCommand::Disassembly::Disassembly(InterruptState &state)
205  : m_nInstructions(0), m_nFirstInstruction(0), m_nIp(0),
206  m_LastLine(0xFFFFFFFF), m_LastInstructionLocation(0)
207 {
208  // Try and count how many lines of instructions we have.
209  m_nIp = state.getInstructionPointer();
210  uintptr_t nSymStart = 0;
211  KernelElf::instance().globalLookupSymbol(m_nIp, &nSymStart);
212 
213  Disassembler disassembler;
214 #ifdef BITS_64
215  disassembler.setMode(64);
216 #endif
217  disassembler.setLocation(nSymStart);
218 
219  m_nFirstInstruction = nSymStart;
220  uintptr_t nLocation = 0;
221  LargeStaticString text;
222  while (true)
223  {
224  nLocation = disassembler.getLocation();
225  text.clear();
226  disassembler.disassemble(text);
227  uintptr_t nSym;
228  KernelElf::instance().globalLookupSymbol(nLocation, &nSym);
229  if (nSym == nLocation && nSym != nSymStart) // New symbol. Quit.
230  break;
231  m_nInstructions++;
232  }
233 }
234 
235 const char *TraceCommand::Disassembly::getLine1(
236  size_t index, DebuggerIO::Colour &colour, DebuggerIO::Colour &bgColour)
237 {
238  static LargeStaticString sym;
239  if (index == 0)
240  {
241  // We treat index == 0 slightly differently - it's the symbol name.
242  colour = DebuggerIO::Yellow;
243  uintptr_t nSym;
244  const char *pSym = KernelElf::instance().globalLookupSymbol(
245  m_nFirstInstruction, &nSym);
246  sym.clear();
247  demangle_full(LargeStaticString(pSym), sym);
248  sym += ":";
249  return sym;
250  }
251  index--; // Get rid of index 0.
252 
253  size_t nInstruction = 0;
254 
255  Disassembler disassembler;
256 #ifdef BITS_64
257  disassembler.setMode(64);
258 #endif
259  LargeStaticString text;
260  uintptr_t nLocation = 0;
261  // If the stored line counter is the one before ours, we can just take that
262  // and advance by one.
263  if (index > 0 && m_LastLine == index - 1)
264  {
265  disassembler.setLocation(m_LastInstructionLocation);
266  // Disassemble the (last) instruction.
267  disassembler.disassemble(text);
268  // Disassemble this instruction.
269  nLocation = disassembler.getLocation();
270  disassembler.disassemble(text);
271  }
272  else
273  {
274  // Disassemble this instruction.
275  disassembler.setLocation(m_nFirstInstruction);
276 
277  while (nInstruction <= index)
278  {
279  nLocation = disassembler.getLocation();
280  disassembler.disassemble(text);
281  nInstruction++;
282  }
283  }
284  // Set the stored line counter.
285  m_LastLine = index;
286  m_LastInstructionLocation = nLocation;
287 
288  // If this is our actual instruction location, put a blue background over
289  // it.
290  if (nLocation == m_nIp)
291  bgColour = DebuggerIO::Blue;
292 
293  // We want grey text.
294  colour = DebuggerIO::DarkGrey;
295 
296  // The text being...
297  sym.clear();
298  sym.append(nLocation, 16, sizeof(uintptr_t) * 2, '0');
299  sym += ' ';
300  return sym;
301 }
302 
303 const char *TraceCommand::Disassembly::getLine2(
304  size_t index, size_t &colOffset, DebuggerIO::Colour &colour,
305  DebuggerIO::Colour &bgColour)
306 {
307  static LargeStaticString sym;
308  if (index == 0)
309  {
310  // We treat index == 0 slightly differently - it's the symbol name.
311  return 0;
312  }
313  index--; // Get rid of index 0.
314 
315  size_t nInstruction = 0;
316 
317  Disassembler disassembler;
318 #ifdef BITS_64
319  disassembler.setMode(64);
320 #endif
321 
322  uintptr_t nLocation = 0;
323  LargeStaticString text;
324  if (index == m_LastLine)
325  {
326  disassembler.setLocation(m_LastInstructionLocation);
327  nLocation = m_LastInstructionLocation;
328  disassembler.disassemble(text);
329  }
330  else
331  {
332  disassembler.setLocation(m_nFirstInstruction);
333  while (nInstruction <= index)
334  {
335  text.clear();
336  nLocation = disassembler.getLocation();
337  disassembler.disassemble(text);
338  nInstruction++;
339  }
340  }
341 
342  // If this is our actual instruction location, put a blue background over
343  // it.
344  if (nLocation == m_nIp)
345  bgColour = DebuggerIO::Blue;
346 
347  // We want white text.
348  colour = DebuggerIO::White;
349 
350  // At offset...
351  colOffset = sizeof(uintptr_t) * 2 + 1;
352 
353  // The text being...
354  sym.clear();
355  sym += text;
356  uint32_t nLen = sym.length() + colOffset;
357  while (nLen++ < width())
358  sym += ' ';
359  return sym;
360 }
361 
362 size_t TraceCommand::Disassembly::getLineCount()
363 {
364  return m_nInstructions + 1;
365 }
366 
367 TraceCommand::Registers::Registers(InterruptState &state) : m_State(state)
368 {
369 }
370 
371 const char *TraceCommand::Registers::getLine1(
372  size_t index, DebuggerIO::Colour &colour, DebuggerIO::Colour &bgColour)
373 {
374  static LargeStaticString str;
375  colour = DebuggerIO::Yellow;
376  // We treat index 0 slightly differently.
377  if (index == 0)
378  {
379  str.clear();
380  if (m_State.kernelMode())
381  str = "Kernel mode";
382  else
383  str = "User mode";
384  return str;
385  }
386  else
387  {
388  int nRegister = index - 1;
389  str.clear();
390  str = m_State.getRegisterName(nRegister);
391  return str;
392  }
393 }
394 
395 const char *TraceCommand::Registers::getLine2(
396  size_t index, size_t &colOffset, DebuggerIO::Colour &colour,
397  DebuggerIO::Colour &bgColour)
398 {
399  static LargeStaticString str;
400  // We treat index 0 slightly differently.
401  if (index == 0)
402  {
403  return 0;
404  }
405  else
406  {
407  int nRegister = index - 1;
408  str.clear();
409  uintptr_t nValue = m_State.getRegister(nRegister);
410  str = "0x";
411  str.append(nValue, 16, sizeof(uintptr_t) * 2, '0');
412  colOffset = width() - 2 * sizeof(uintptr_t) - 2;
413  return str;
414  }
415 }
416 
417 size_t TraceCommand::Registers::getLineCount()
418 {
419  return m_State.getRegisterCount() + 1;
420 }
421 
422 TraceCommand::Stacktrace::Stacktrace(InterruptState &state) : m_Bt()
423 {
424  m_Bt.performBacktrace(state);
425 }
426 
427 const char *TraceCommand::Stacktrace::getLine1(
428  size_t index, DebuggerIO::Colour &colour, DebuggerIO::Colour &bgColour)
429 {
430  static HugeStaticString str;
431  str.clear();
432  m_Bt.prettyPrint(str, 1, index);
433  if (str.length())
434  str = str.left(str.length() - 1);
435  return str;
436 }
437 
438 const char *TraceCommand::Stacktrace::getLine2(
439  size_t index, size_t &colOffset, DebuggerIO::Colour &colour,
440  DebuggerIO::Colour &bgColour)
441 {
442  return 0;
443 }
444 
445 size_t TraceCommand::Stacktrace::getLineCount()
446 {
447  return m_Bt.numStackFrames();
448 }
virtual size_t getWidth()=0
virtual void enableCli()=0
void autocomplete(const HugeStaticString &input, HugeStaticString &output)
Definition: TraceCommand.cc:36
virtual char getChar()=0
static KernelElf & instance()
Definition: KernelElf.h:129
virtual void enableRefreshes()=0
static void setSingleStep(bool bEnable, InterruptState &state)
uintptr_t globalLookupSymbol(const char *pName)
Definition: KernelElf.cc:1031
virtual void drawString(const char *str, size_t row, size_t col, Colour foreColour, Colour backColour)=0
bool execute(const HugeStaticString &input, HugeStaticString &output, InterruptState &state, DebuggerIO *screen)
Definition: TraceCommand.cc:46
void setLocation(uintptr_t nLocation)
void disassemble(LargeStaticString &text)
virtual void drawHorizontalLine(char c, size_t row, size_t colStart, size_t colEnd, Colour foreColour, Colour backColour)=0