[tools] Add j6threads gdb command
The j6threads command shows the current thread, ready threads, and blocked threads for a given CPU. To support this, TCB structs gained a pointer to their thread (instead of trying to do offset magic) and threads gained a pointer to their creator. Also removed thread::from_tcb() now that the TCB has a pointer.
This commit is contained in:
@@ -26,6 +26,25 @@ class PrintStackCommand(gdb.Command):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
def stack_walk(frame, depth):
|
||||||
|
for i in range(depth-1, -1, -1):
|
||||||
|
ret = gdb.parse_and_eval(f"*(uint64_t*)({frame:#x} + 0x8)")
|
||||||
|
|
||||||
|
name = ""
|
||||||
|
try:
|
||||||
|
block = gdb.block_for_pc(int(ret))
|
||||||
|
if block:
|
||||||
|
name = block.function or ""
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print("{:016x}: {:016x} {}".format(int(frame), int(ret), name))
|
||||||
|
|
||||||
|
frame = int(gdb.parse_and_eval(f"*(uint64_t*)({frame:#x})"))
|
||||||
|
if frame == 0 or ret == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
class PrintBacktraceCommand(gdb.Command):
|
class PrintBacktraceCommand(gdb.Command):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__("j6bt", gdb.COMMAND_DATA)
|
super().__init__("j6bt", gdb.COMMAND_DATA)
|
||||||
@@ -43,22 +62,7 @@ class PrintBacktraceCommand(gdb.Command):
|
|||||||
if len(args) > 1:
|
if len(args) > 1:
|
||||||
depth = int(gdb.parse_and_eval(args[1]))
|
depth = int(gdb.parse_and_eval(args[1]))
|
||||||
|
|
||||||
for i in range(depth-1, -1, -1):
|
stack_walk(frame, depth)
|
||||||
ret = gdb.parse_and_eval(f"*(uint64_t*)({frame:#x} + 0x8)")
|
|
||||||
|
|
||||||
name = ""
|
|
||||||
try:
|
|
||||||
block = gdb.block_for_pc(int(ret))
|
|
||||||
if block:
|
|
||||||
name = block.function or ""
|
|
||||||
except RuntimeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
print("{:016x}: {:016x} {}".format(int(frame), int(ret), name))
|
|
||||||
|
|
||||||
frame = int(gdb.parse_and_eval(f"*(uint64_t*)({frame:#x})"))
|
|
||||||
if frame == 0 or ret == 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class TableWalkCommand(gdb.Command):
|
class TableWalkCommand(gdb.Command):
|
||||||
@@ -128,10 +132,78 @@ class TableWalkCommand(gdb.Command):
|
|||||||
table = (entry & 0x7ffffffffffffe00) | 0xffffc00000000000
|
table = (entry & 0x7ffffffffffffe00) | 0xffffc00000000000
|
||||||
|
|
||||||
|
|
||||||
|
class GetThreadsCommand(gdb.Command):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("j6threads", gdb.COMMAND_DATA)
|
||||||
|
|
||||||
|
def print_thread(self, addr):
|
||||||
|
if addr == 0:
|
||||||
|
print(" <no thread>\n")
|
||||||
|
return
|
||||||
|
|
||||||
|
tcb = f"((TCB*){addr:#x})"
|
||||||
|
thread = f"({tcb}->thread)"
|
||||||
|
stack = int(gdb.parse_and_eval(f"{tcb}->kernel_stack"))
|
||||||
|
rsp = int(gdb.parse_and_eval(f"{tcb}->rsp"))
|
||||||
|
pri = int(gdb.parse_and_eval(f"{tcb}->priority"))
|
||||||
|
koid = int(gdb.parse_and_eval(f"{thread}->m_koid"))
|
||||||
|
proc = int(gdb.parse_and_eval(f"{thread}->m_parent.m_koid"))
|
||||||
|
|
||||||
|
creator = int(gdb.parse_and_eval(f"{thread}->m_creator"))
|
||||||
|
if creator != 0:
|
||||||
|
creator_koid = int(gdb.parse_and_eval(f"{thread}->m_creator->m_koid"))
|
||||||
|
creator = f"{creator_koid:x}"
|
||||||
|
else:
|
||||||
|
creator = "<no thread>"
|
||||||
|
|
||||||
|
print(f" Thread {proc:x}:{koid:x}")
|
||||||
|
print(f" creator: {creator}")
|
||||||
|
print(f" priority: {pri}")
|
||||||
|
print(f" kstack: {stack:#x}")
|
||||||
|
print(f" rsp: {rsp:#x}")
|
||||||
|
print("------------------------------------")
|
||||||
|
|
||||||
|
if stack != 0:
|
||||||
|
rsp = int(gdb.parse_and_eval(f"{tcb}->rsp"))
|
||||||
|
stack_walk(rsp + 5*8, 5)
|
||||||
|
|
||||||
|
print("")
|
||||||
|
|
||||||
|
def print_thread_list(self, addr, name):
|
||||||
|
if addr == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"=== {name} ===")
|
||||||
|
|
||||||
|
while addr != 0:
|
||||||
|
self.print_thread(addr)
|
||||||
|
addr = int(gdb.parse_and_eval(f"((tcb_node*){addr:#x})->m_next"))
|
||||||
|
|
||||||
|
|
||||||
|
def invoke(self, arg, from_tty):
|
||||||
|
args = gdb.string_to_argv(arg)
|
||||||
|
if len(args) != 1:
|
||||||
|
raise RuntimeError("Usage: j6threads <cpu>")
|
||||||
|
|
||||||
|
ncpu = int(args[0])
|
||||||
|
runlist = f"scheduler::s_instance->m_run_queues.m_elements[{ncpu:#x}]"
|
||||||
|
|
||||||
|
print("CURRENT:")
|
||||||
|
current = int(gdb.parse_and_eval(f"{runlist}.current"))
|
||||||
|
self.print_thread(current)
|
||||||
|
|
||||||
|
for pri in range(8):
|
||||||
|
ready = int(gdb.parse_and_eval(f"{runlist}.ready[{pri:#x}].m_head"))
|
||||||
|
self.print_thread_list(ready, f"PRIORITY {pri}")
|
||||||
|
|
||||||
|
blocked = int(gdb.parse_and_eval(f"{runlist}.blocked.m_head"))
|
||||||
|
self.print_thread_list(ready, "BLOCKED")
|
||||||
|
|
||||||
PrintStackCommand()
|
PrintStackCommand()
|
||||||
PrintBacktraceCommand()
|
PrintBacktraceCommand()
|
||||||
TableWalkCommand()
|
TableWalkCommand()
|
||||||
|
GetThreadsCommand()
|
||||||
|
|
||||||
gdb.execute("target remote :1234")
|
|
||||||
gdb.execute("display/i $rip")
|
gdb.execute("display/i $rip")
|
||||||
|
if not gdb.selected_inferior().was_attached:
|
||||||
|
gdb.execute("target remote :1234")
|
||||||
|
|||||||
@@ -9,7 +9,9 @@
|
|||||||
#include "idt.h"
|
#include "idt.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "msr.h"
|
#include "msr.h"
|
||||||
|
#include "objects/thread.h"
|
||||||
#include "objects/vm_area.h"
|
#include "objects/vm_area.h"
|
||||||
|
#include "scheduler.h"
|
||||||
#include "syscall.h"
|
#include "syscall.h"
|
||||||
#include "tss.h"
|
#include "tss.h"
|
||||||
|
|
||||||
@@ -60,10 +62,6 @@ cpu_early_init(cpu_data *cpu)
|
|||||||
|
|
||||||
// Install the GS base pointint to the cpu_data
|
// Install the GS base pointint to the cpu_data
|
||||||
wrmsr(msr::ia32_gs_base, reinterpret_cast<uintptr_t>(cpu));
|
wrmsr(msr::ia32_gs_base, reinterpret_cast<uintptr_t>(cpu));
|
||||||
|
|
||||||
// Set the initial process as the kernel "process"
|
|
||||||
extern process &g_kernel_process;
|
|
||||||
cpu->process = &g_kernel_process;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -74,6 +72,18 @@ cpu_init(cpu_data *cpu, bool bsp)
|
|||||||
cpu_early_init(cpu);
|
cpu_early_init(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the initial process as the kernel "process"
|
||||||
|
extern process &g_kernel_process;
|
||||||
|
cpu->process = &g_kernel_process;
|
||||||
|
|
||||||
|
thread *idle = thread::create_idle_thread(
|
||||||
|
g_kernel_process,
|
||||||
|
scheduler::max_priority,
|
||||||
|
cpu->rsp0);
|
||||||
|
|
||||||
|
cpu->thread = idle;
|
||||||
|
cpu->tcb = idle->tcb();
|
||||||
|
|
||||||
// Set up the syscall MSRs
|
// Set up the syscall MSRs
|
||||||
syscall_enable();
|
syscall_enable();
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,14 @@ thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) :
|
|||||||
{
|
{
|
||||||
parent.space().initialize_tcb(m_tcb);
|
parent.space().initialize_tcb(m_tcb);
|
||||||
m_tcb.priority = pri;
|
m_tcb.priority = pri;
|
||||||
|
m_tcb.thread = this;
|
||||||
|
|
||||||
if (!rsp0)
|
if (!rsp0)
|
||||||
setup_kernel_stack();
|
setup_kernel_stack();
|
||||||
else
|
else
|
||||||
m_tcb.rsp0 = rsp0;
|
m_tcb.rsp0 = rsp0;
|
||||||
|
|
||||||
|
m_creator = current_cpu().thread;
|
||||||
m_self_handle = parent.add_handle(this);
|
m_self_handle = parent.add_handle(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,14 +40,6 @@ thread::~thread()
|
|||||||
g_kernel_stacks.return_section(m_tcb.kernel_stack);
|
g_kernel_stacks.return_section(m_tcb.kernel_stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
thread *
|
|
||||||
thread::from_tcb(TCB *tcb)
|
|
||||||
{
|
|
||||||
static ptrdiff_t offset =
|
|
||||||
-1 * static_cast<ptrdiff_t>(offsetof(thread, m_tcb));
|
|
||||||
return reinterpret_cast<thread*>(util::offset_pointer(tcb, offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
thread & thread::current() { return *current_cpu().thread; }
|
thread & thread::current() { return *current_cpu().thread; }
|
||||||
|
|
||||||
inline void schedule_if_current(thread *t) { if (t == current_cpu().thread) scheduler::get().schedule(); }
|
inline void schedule_if_current(thread *t) { if (t == current_cpu().thread) scheduler::get().schedule(); }
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ struct TCB
|
|||||||
uintptr_t rsp3;
|
uintptr_t rsp3;
|
||||||
uintptr_t rflags3;
|
uintptr_t rflags3;
|
||||||
uintptr_t pml4;
|
uintptr_t pml4;
|
||||||
|
// End of area used by asembly
|
||||||
|
|
||||||
|
thread* thread;
|
||||||
|
|
||||||
uint8_t priority;
|
uint8_t priority;
|
||||||
// note: 3 bytes padding
|
// note: 3 bytes padding
|
||||||
@@ -46,9 +49,6 @@ public:
|
|||||||
none = 0x00
|
none = 0x00
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Get the pointer to the thread object containing this TCB
|
|
||||||
static thread * from_tcb(TCB *tcb);
|
|
||||||
|
|
||||||
/// Destructor
|
/// Destructor
|
||||||
virtual ~thread();
|
virtual ~thread();
|
||||||
|
|
||||||
@@ -175,6 +175,7 @@ private:
|
|||||||
tcb_node m_tcb;
|
tcb_node m_tcb;
|
||||||
|
|
||||||
process &m_parent;
|
process &m_parent;
|
||||||
|
thread *m_creator;
|
||||||
|
|
||||||
state m_state;
|
state m_state;
|
||||||
wait_type m_wait_type;
|
wait_type m_wait_type;
|
||||||
|
|||||||
@@ -97,16 +97,8 @@ scheduler::start()
|
|||||||
|
|
||||||
{
|
{
|
||||||
util::scoped_lock lock {queue.lock};
|
util::scoped_lock lock {queue.lock};
|
||||||
|
thread *idle = cpu.thread;
|
||||||
process *kp = &process::kernel_process();
|
queue.current = idle->tcb();
|
||||||
thread *idle = thread::create_idle_thread(*kp, max_priority, cpu.rsp0);
|
|
||||||
|
|
||||||
auto *tcb = idle->tcb();
|
|
||||||
cpu.process = kp;
|
|
||||||
cpu.thread = idle;
|
|
||||||
cpu.tcb = tcb;
|
|
||||||
|
|
||||||
queue.current = tcb;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu.apic->enable_timer(isr::isrTimer, false);
|
cpu.apic->enable_timer(isr::isrTimer, false);
|
||||||
@@ -130,7 +122,7 @@ void scheduler::prune(run_queue &queue, uint64_t now)
|
|||||||
// move them to the appropriate lists.
|
// move them to the appropriate lists.
|
||||||
auto *tcb = queue.blocked.front();
|
auto *tcb = queue.blocked.front();
|
||||||
while (tcb) {
|
while (tcb) {
|
||||||
thread *th = thread::from_tcb(tcb);
|
thread *th = tcb->thread;
|
||||||
uint8_t priority = tcb->priority;
|
uint8_t priority = tcb->priority;
|
||||||
|
|
||||||
bool ready = th->has_state(thread::state::ready);
|
bool ready = th->has_state(thread::state::ready);
|
||||||
@@ -171,7 +163,7 @@ scheduler::check_promotions(run_queue &queue, uint64_t now)
|
|||||||
{
|
{
|
||||||
for (auto &pri_list : queue.ready) {
|
for (auto &pri_list : queue.ready) {
|
||||||
for (auto *tcb : pri_list) {
|
for (auto *tcb : pri_list) {
|
||||||
const thread *th = thread::from_tcb(queue.current);
|
const thread *th = queue.current->thread;
|
||||||
const bool constant = th->has_state(thread::state::constant);
|
const bool constant = th->has_state(thread::state::constant);
|
||||||
if (constant)
|
if (constant)
|
||||||
continue;
|
continue;
|
||||||
@@ -266,7 +258,7 @@ scheduler::schedule()
|
|||||||
queue.lock.acquire(&waiter);
|
queue.lock.acquire(&waiter);
|
||||||
|
|
||||||
queue.current->time_left = remaining;
|
queue.current->time_left = remaining;
|
||||||
thread *th = thread::from_tcb(queue.current);
|
thread *th = queue.current->thread;
|
||||||
uint8_t priority = queue.current->priority;
|
uint8_t priority = queue.current->priority;
|
||||||
const bool constant = th->has_state(thread::state::constant);
|
const bool constant = th->has_state(thread::state::constant);
|
||||||
|
|
||||||
@@ -313,7 +305,7 @@ scheduler::schedule()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread *next_thread = thread::from_tcb(next);
|
thread *next_thread = next->thread;
|
||||||
|
|
||||||
cpu.thread = next_thread;
|
cpu.thread = next_thread;
|
||||||
cpu.process = &next_thread->parent();
|
cpu.process = &next_thread->parent();
|
||||||
|
|||||||
Reference in New Issue
Block a user