The Pedigree Project 0.1
ThreadsCommand.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 <config.h>
21
22#if THREADS
23
24#include "pedigree/kernel/debugger/DebuggerIO.h"
25#include "pedigree/kernel/debugger/commands/ThreadWaitDiagnostic.h"
26#include "pedigree/kernel/debugger/commands/ThreadsCommand.h"
27#include "pedigree/kernel/linker/KernelElf.h"
28#include "pedigree/kernel/process/Process.h"
29#include "pedigree/kernel/process/Scheduler.h"
30#include "pedigree/kernel/process/Semaphore.h"
31#include "pedigree/kernel/process/Thread.h"
32#include "pedigree/kernel/processor/Processor.h"
33#include "pedigree/kernel/processor/ProcessorInformation.h"
34#include "pedigree/kernel/processor/state.h"
35#include "pedigree/kernel/utilities/demangle.h"
36#include "pedigree/kernel/utilities/utility.h"
37
39 : DebuggerCommand(), Scrollable(), m_SelectedLine(0), m_nLines(0) {}
40
42
43static const char* debugStateName(Thread::DebugState state) {
44 switch (state) {
45 case Thread::SemWait:
46 return "Sem-Wait";
47 case Thread::CondWait:
48 return "Cond-Wait";
49 case Thread::Joining:
50 return "Joining";
51 case Thread::FutexWait:
52 return "Futex-Wait";
53 case Thread::EventWait:
54 return "Event-Wait";
55 case Thread::ProcessWait:
56 return "Process-Wait";
57 case Thread::CallbackDrain:
58 return "Callback-Drain";
59 case Thread::None:
60 return nullptr;
61 }
62 return "<unknown DebugState>";
63}
64
65static void appendWaitState(Thread* thread, HugeStaticString& line) {
66 ThreadWaitDiagnostic diagnostic;
67 diagnostic.sleeping = thread->getStatus() == Thread::Sleeping;
68
70 if (!thread->getWaitDebugInfo(wait)) {
71 appendThreadWaitDiagnostic(diagnostic, line);
72 return;
73 }
74
75 diagnostic.hasWait = true;
76 diagnostic.queue = reinterpret_cast<uintptr_t>(wait.queue);
77 diagnostic.channelOwner = reinterpret_cast<uintptr_t>(wait.channelOwner);
78 diagnostic.channelValue = wait.channelValue;
79 diagnostic.reason = wait.reason;
80 diagnostic.stateLevel = wait.stateLevel;
81 diagnostic.queued = wait.queued;
82
83 uintptr_t debugAddress = 0;
84 if (thread->getDebugState(debugAddress) == Thread::SemWait && wait.channelOwner) {
85 const Semaphore* semaphore = reinterpret_cast<const Semaphore*>(wait.channelOwner);
86 const void* owner = semaphore->getDebugMutexOwner();
87 if (owner) {
88 diagnostic.mutexOwner = reinterpret_cast<uintptr_t>(owner);
89 }
90 }
91
92 appendThreadWaitDiagnostic(diagnostic, line);
93}
94
95static void selectLine(size_t index, Scheduler::ProcessLease& selectedProcess,
96 Process::ThreadLease& selectedThread) {
97 size_t line = 0;
98 for (size_t i = 0; i < Scheduler::instance().getNumProcesses(); ++i) {
100 if (!Scheduler::instance().acquireProcess(process, i)) {
101 continue;
102 }
103
104 if (index == line) {
105 selectedProcess = pedigree_std::move(process);
106 return;
107 }
108 ++line;
109
110 for (size_t j = 0; j < process->getNumThreads(); ++j) {
112 const bool threadAcquired = process->acquireThread(thread, j);
113 if (index == line) {
114 if (threadAcquired) {
115 selectedProcess = pedigree_std::move(process);
116 selectedThread = pedigree_std::move(thread);
117 }
118 return;
119 }
120 ++line;
121 }
122 }
123}
124
126
128 InterruptState& state, DebuggerIO* pScreen) {
129 // How many lines do we have?
130 m_nLines = 0;
131 for (size_t i = 0; i < Scheduler::instance().getNumProcesses(); i++) {
133 if (!Scheduler::instance().acquireProcess(process, i)) {
134 continue;
135 }
136 m_nLines++; // For the process.
137 m_nLines += process->getNumThreads();
138 }
139
140 // Let's enter 'raw' screen mode.
141 pScreen->disableCli();
142
143 // Initialise the Scrollable class
144 move(0, 1);
145 resize(pScreen->getWidth(), pScreen->getHeight() - 2);
146 setScrollKeys('j', 'k');
147
148 // Clear the top status lines.
149 pScreen->drawHorizontalLine(' ', 0, 0, pScreen->getWidth() - 1, DebuggerIO::White,
150 DebuggerIO::Green);
151
152 // Write the correct text in the upper status line.
153 pScreen->drawString("Pedigree debugger - Thread selector", 0, 0, DebuggerIO::White,
154 DebuggerIO::Green);
155
156 // Clear the bottom status lines.
157 // TODO: If we use arrow keys and page up/down keys we actually can remove
158 // the status line
159 // because the interface is then intuitive enough imho.
160 pScreen->drawHorizontalLine(' ', pScreen->getHeight() - 1, 0, pScreen->getWidth() - 1,
161 DebuggerIO::White, DebuggerIO::Green);
162
163 // Write some helper text in the lower status line.
164 // TODO FIXME: Drawing this might screw the top status bar
165 pScreen->drawString(
166 "backspace: Page up. space: Page down. q: Quit. enter: Switch to "
167 "thread",
168 pScreen->getHeight() - 1, 0, DebuggerIO::White, DebuggerIO::Green);
169 pScreen->drawString("backspace", pScreen->getHeight() - 1, 0, DebuggerIO::Yellow,
170 DebuggerIO::Green);
171 pScreen->drawString("space", pScreen->getHeight() - 1, 20, DebuggerIO::Yellow, DebuggerIO::Green);
172 pScreen->drawString("q", pScreen->getHeight() - 1, 38, DebuggerIO::Yellow, DebuggerIO::Green);
173 pScreen->drawString("enter", pScreen->getHeight() - 1, 47, DebuggerIO::Yellow, DebuggerIO::Green);
174
175 // Main loop.
176 bool bStop = false;
177 bool bReturn = true;
178 while (!bStop) {
179 refresh(pScreen);
180
181 // Wait for input.
182 char c = 0;
183 while (!(c = pScreen->getChar()))
184 ;
185
186 // TODO: Use arrow keys and page up/down someday
187 if (c == 'j') {
188 scroll(-1);
189 if (static_cast<ssize_t>(m_SelectedLine) - 1 >= 0)
190 m_SelectedLine--;
191 } else if (c == 'k') {
192 scroll(1);
193 if (m_SelectedLine + 1 < getLineCount())
194 m_SelectedLine++;
195 } else if (c == ' ') {
196 scroll(static_cast<ssize_t>(height()));
197 if (m_SelectedLine + height() < getLineCount())
198 m_SelectedLine += height();
199 else
200 m_SelectedLine = getLineCount() - 1;
201 } else if (c == 0x08) {
202 scroll(-static_cast<ssize_t>(height()));
203 if (static_cast<ssize_t>(m_SelectedLine) - static_cast<ssize_t>(height()) >= 0)
204 m_SelectedLine -= height();
205 else
206 m_SelectedLine = 0;
207 } else if (c == '\n' || c == '\r') {
208 // if(swapThread(state, pScreen))
209 // {
210 // bStop = true;
211 // bReturn = false;
212 // }
213 } else if (c == 'q')
214 bStop = true;
215 }
216
217 // HACK:: Serial connections will fill the screen with the last background
218 // colour used.
219 // Here we write a space with black background so the CLI screen
220 // doesn't get filled by some random colour!
221 pScreen->drawString(" ", 1, 0, DebuggerIO::White, DebuggerIO::Black);
222 pScreen->enableCli();
223 return bReturn;
224}
225
226const char* ThreadsCommand::getLine1(size_t index, DebuggerIO::Colour& colour,
227 DebuggerIO::Colour& bgColour) {
228 static NormalStaticString Line;
229 Line.clear();
230
233 selectLine(index, process, thread);
234 Process* tehProcess = process.get();
235 Thread* tehThread = thread.get();
236
237 if (!tehProcess) {
238 // No processes, or the index does not match.
239 return Line;
240 }
241
242 // If this is just a process line.
243 colour = DebuggerIO::Yellow;
244 if (index == m_SelectedLine)
245 bgColour = DebuggerIO::Blue;
246 else
247 bgColour = DebuggerIO::Black;
248 if (tehThread == 0) {
249 Line += "[";
250 Line += tehProcess->getId();
251 Line += "] ";
252 Line += tehProcess->description();
253 } else {
254 Line += " | ";
255 }
256
257 return Line;
258}
259const char* ThreadsCommand::getLine2(size_t index, size_t& colOffset, DebuggerIO::Colour& colour,
260 DebuggerIO::Colour& bgColour) {
261 static HugeStaticString Line;
262 Line.clear();
263
266 selectLine(index, process, thread);
267 Thread* tehThread = thread.get();
268
269 if (tehThread != 0 && tehThread != Processor::information().getCurrentThread()) {
270 Thread::Status status = tehThread->getStatus();
271 switch (status) {
272 case Thread::Created:
273 Line += "C";
274 break;
275 case Thread::Running:
276 Line += "r";
277 break;
278 case Thread::Ready:
279 Line += "R";
280 break;
281 case Thread::Sleeping:
282 Line += "S";
283 break;
284 case Thread::Zombie:
285 Line += "Z";
286 break;
287 case Thread::AwaitingJoin:
288 Line += "J";
289 break;
290 }
291
292 Line += "[";
293 Line += "CPU";
294 Line += tehThread->getCpuId();
295 Line += ":";
296 Line += tehThread->getId();
297 Line += "] ";
298
299 uintptr_t ip = 0;
300 Thread::DebugState state = tehThread->getDebugState(ip);
301 if (state != Thread::None) {
302 Line += debugStateName(state);
303 Line += " @ ";
304 Line.append(ip, 16);
305
306 uintptr_t symStart;
307 const char* pSym = KernelElf::instance().globalLookupSymbol(ip, &symStart);
308 if (pSym) {
310 demangle_full(LargeStaticString(pSym), sym);
311 Line += ": ";
312 Line += sym;
313 }
314 } else {
315 // Extract the last instruction pointer from the scheduler state
316 const SchedulerState& state = tehThread->state();
317 ip = state.getInstructionPointer();
318
319 Line += " @ ";
320 Line.append(ip, 16);
321
322 uintptr_t symStart;
323 const char* pSym = KernelElf::instance().globalLookupSymbol(ip, &symStart);
324 if (pSym) {
326 demangle_full(LargeStaticString(pSym), sym);
327 Line += ": ";
328 Line += sym;
329 }
330 }
331 appendWaitState(tehThread, Line);
332 colour = DebuggerIO::LightGrey;
333 } else if (tehThread != 0) // tehThread == g_pCurrentThread
334 {
335 Thread::Status status = tehThread->getStatus();
336 switch (status) {
337 case Thread::Created:
338 Line += "C";
339 break;
340 case Thread::Running:
341 Line += "r";
342 break;
343 case Thread::Ready:
344 Line += "R";
345 break;
346 case Thread::Sleeping:
347 Line += "S";
348 break;
349 case Thread::Zombie:
350 Line += "Z";
351 break;
352 case Thread::AwaitingJoin:
353 Line += "J";
354 break;
355 }
356 Line += "[";
357 Line += "CPU";
358 Line += tehThread->getCpuId();
359 Line += ":";
360 Line += tehThread->getId();
361 Line += "] - CURRENT ";
362
363 uintptr_t ip = 0;
364 Thread::DebugState state = tehThread->getDebugState(ip);
365 if (state != Thread::None) {
366 Line += debugStateName(state);
367 Line += " @ ";
368 Line.append(ip, 16);
369
370 uintptr_t symStart;
371 const char* pSym = KernelElf::instance().globalLookupSymbol(ip, &symStart);
372 if (pSym) {
374 demangle_full(LargeStaticString(pSym), sym);
375 Line += ": ";
376 Line += sym;
377 }
378 }
379 appendWaitState(tehThread, Line);
380
381 colour = DebuggerIO::Yellow;
382 }
383
384 if (index == m_SelectedLine)
385 bgColour = DebuggerIO::Blue;
386 else
387 bgColour = DebuggerIO::Black;
388 colOffset = 3;
389 return Line;
390}
391
392size_t ThreadsCommand::getLineCount() {
393 return m_nLines;
394}
395
396bool ThreadsCommand::swapThread(InterruptState& state, DebuggerIO* pScreen) {
399 selectLine(m_SelectedLine, process, thread);
400 Thread* tehThread = thread.get();
401
402 // We can only swap to threads, not entire processes!
403 if (tehThread == 0)
404 return false;
405
406 pScreen->destroy();
407 // Scheduler::instance().switchToAndDebug(state, tehThread);
408
409 return true;
410}
411
412#endif
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)
size_t getId()
Definition Process.h:499
size_t getNumThreads()
Definition Process.cc:1380
LargeStaticString & description()
Definition Process.h:525
MUST_USE_RESULT bool acquireThread(ThreadLease &lease, size_t n)
Definition Process.cc:1385
static ProcessorInformation & information()
static Scheduler & instance()
Definition Scheduler.h:96
size_t getNumProcesses()
Definition Scheduler.cc:268
MUST_USE_RESULT bool acquireProcess(ProcessLease &lease, size_t n)
Definition Scheduler.cc:275
const void * getDebugMutexOwner() const
Definition Semaphore.cc:605
SchedulerState & state()
Definition Thread.cc:832
DebugState
Definition Thread.h:177
bool getWaitDebugInfo(WaitDebugInfo &info)
Definition Thread.cc:3163
Status getStatus() const
Definition Thread.h:433
DebugState getDebugState(uintptr_t &address)
Definition Thread.h:574
size_t getId()
Definition Thread.h:465
size_t getCpuId()
Definition Thread.h:865
void autocomplete(const HugeStaticString &input, HugeStaticString &output)
bool execute(const HugeStaticString &input, HugeStaticString &output, InterruptState &state, DebuggerIO *screen)