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