capDL: Add kernel debugging tool for capDL

Add general debugging features of capDL Kernel Printing

Signed-off-by: Jingyao Zhou <Jingyao.Zhou@data61.csiro.au>
This commit is contained in:
Jingyao Zhou 2020-09-16 18:01:36 +10:00
parent 658e110622
commit a403d0d66c
3 changed files with 562 additions and 0 deletions

View file

@ -6,3 +6,45 @@
#pragma once
#ifdef CONFIG_DEBUG_BUILD
/* helpers */
void add_to_seen(cap_t c);
void reset_seen_list(void);
bool_t seen(cap_t c);
bool_t same_cap(cap_t a, cap_t b);
bool_t root_or_idle_tcb(tcb_t *tcb);
/* common */
void obj_tcb_print_cnodes(cap_t cnode, tcb_t *tcb);
void print_caps(void);
void print_objects(void);
void print_cap(cap_t cap);
void print_object(cap_t cap);
void obj_tcb_print_attrs(tcb_t *tcb);
void obj_sc_print_attrs(cap_t sc);
void obj_cnode_print_attrs(cap_t cnode);
void obj_ut_print_attrs(cte_t *slot, tcb_t *tcb);
void obj_tcb_print_slots(tcb_t *tcb);
void obj_cnode_print_slots(tcb_t *tcb);
void obj_irq_print_slots(cap_t irq_cap);
void obj_irq_print_maps(void);
void cap_ep_print_attrs(cap_t ep);
void cap_ntfn_print_attrs(cap_t ntfn);
void cap_cnode_print_attrs(cap_t cnode);
/* arch specific functions */
void print_ipc_buffer_slot(tcb_t *tcb);
/* TBD: currently the capDL extractor declaring an object for every entry in the vspace.
* However, frames can be mapped into multiple locations but sould only be declared once.
*/
void obj_vtable_print_slots(tcb_t *tcb);
word_t get_tcb_sp(tcb_t *tcb);
void print_cap_arch(cap_t cap);
void print_object_arch(cap_t cap);
void obj_tcb_print_vtable(tcb_t *tcb);
#endif

View file

@ -46,3 +46,8 @@ add_sources(
src/object/schedcontrol.c
src/kernel/sporadic.c
)
add_sources(
DEP "KernelDebugBuild;KernelArchARM OR KernelSel4ArchX86_64 OR KernelSel4ArchRiscV32"
CFILES src/machine/capdl.c
)

515
src/machine/capdl.c Normal file
View file

@ -0,0 +1,515 @@
/*
* Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#include <machine/capdl.h>
#include <machine/registerset.h>
#include <machine/timer.h>
#include <config.h>
#include <string.h>
#include <kernel/cspace.h>
#ifdef CONFIG_KERNEL_MCS
#include <kernel/sporadic.h>
#endif
#ifdef CONFIG_DEBUG_BUILD
#define SEEN_SZ 256
/* seen list - check this array before we print cnode and vspace */
/* TBD: This is to avoid traversing the same cnode. It should be applied to object
* as well since the extractor might comes across multiple caps to the same object.
*/
cap_t seen_list[SEEN_SZ];
int watermark = 0;
void add_to_seen(cap_t c)
{
/* Won't work well if there're more than SEEN_SZ cnode */
if (watermark <= SEEN_SZ) {
seen_list[watermark] = c;
watermark++;
}
}
void reset_seen_list(void)
{
memset(seen_list, 0, SEEN_SZ * sizeof(seen_list[0]));
watermark = 0;
}
bool_t seen(cap_t c)
{
for (int i = 0; i < watermark; i++) {
if (same_cap(seen_list[i], c)) {
return true;
}
}
return false;
}
bool_t same_cap(cap_t a, cap_t b)
{
return (a.words[0] == b.words[0] && a.words[1] == b.words[1]);
}
/* Return true if strings are the same */
static inline bool_t strings_equal(const char *str1, const char *str2)
{
while (*str1 && *str2 && (*str1 == *str2)) {
str1++;
str2++;
}
return !(*(const unsigned char *)str1 - * (const unsigned char *)str2);
}
/* Return true if the tcb is for rootserver or idle thread */
bool_t root_or_idle_tcb(tcb_t *tcb)
{
return (strings_equal(TCB_PTR_DEBUG_PTR(tcb)->tcbName, "rootserver")
|| strings_equal(TCB_PTR_DEBUG_PTR(tcb)->tcbName, "idle_thread"));
}
/*
* Print objects
*/
void obj_tcb_print_attrs(tcb_t *tcb)
{
printf("(addr: 0x%lx, ip: 0x%lx, sp: 0x%lx, prio: %lu, max_prio: %lu",
(long unsigned int)tcb->tcbIPCBuffer,
(long unsigned int)getRestartPC(tcb),
(long unsigned int)get_tcb_sp(tcb),
(long unsigned int)tcb->tcbPriority,
(long unsigned int)tcb->tcbMCP);
#ifdef ENABLE_SMP_SUPPORT
printf(", affinity: %lu", (long unsigned int)tcb->tcbAffinity);
#endif /* ENABLE_SMP_SUPPORT */
/* init */
#ifdef CONFIG_KERNEL_MCS
cap_t ep_cap = TCB_PTR_CTE_PTR(tcb, tcbFaultHandler)->cap;
if (cap_get_capType(ep_cap) != cap_null_cap) {
printf(", fault_ep: %p", EP_PTR(cap_endpoint_cap_get_capEPPtr(ep_cap)));
}
#endif
printf(", dom: %ld)\n", tcb->tcbDomain);
}
#ifdef CONFIG_KERNEL_MCS
/* to make compilers happy */
#define REFILL_INDEX(sc, index) (((refill_t *) (SC_REF(sc) + sizeof(sched_context_t)))[index])
#define REFILL_HEAD(sc) REFILL_INDEX((sc), (sc)->scRefillHead)
static inline ticks_t sc_get_budget(sched_context_t *sc)
{
ticks_t sum = REFILL_HEAD(sc).rAmount;
word_t current = sc->scRefillHead;
while (current != sc->scRefillTail) {
current = ((current == sc->scRefillMax - 1u) ? (0) : current + 1u);
sum += REFILL_INDEX(sc, current).rAmount;
}
return sum;
}
void obj_sc_print_attrs(cap_t sc_cap)
{
sched_context_t *sc = SC_PTR(cap_sched_context_cap_get_capSCPtr(sc_cap));
printf("(period: %lu, budget: %lu, %lu bits)\n",
(long unsigned int)ticksToUs(sc->scPeriod),
(long unsigned int)ticksToUs(sc_get_budget(sc)),
(word_t)cap_sched_context_cap_get_capSCSizeBits(sc_cap)
);
}
#endif /* CONFIG_KERNEL_MCS */
void obj_ut_print_attrs(cte_t *slot, tcb_t *tcb)
{
/* might have two untypeds with the same address but different size */
printf("%p_%lu_untyped = ut (%lu bits, paddr: %p) {",
(void *)cap_untyped_cap_get_capPtr(slot->cap),
(long unsigned int)cap_untyped_cap_get_capBlockSize(slot->cap),
(long unsigned int)cap_untyped_cap_get_capBlockSize(slot->cap),
WORD_PTR(cap_untyped_cap_get_capPtr(slot->cap)));
/* there is no need to check for a NullCap as NullCaps are
always accompanied by null mdb pointers */
for (cte_t *nextPtr = CTE_PTR(mdb_node_get_mdbNext(slot->cteMDBNode));
nextPtr && isMDBParentOf(slot, nextPtr);
nextPtr = CTE_PTR(mdb_node_get_mdbNext(slot->cteMDBNode))) {
if (!sameRegionAs(slot->cap, nextPtr->cap)) {
/* TBD:
* - this will print out the attributes of the cap, which it shouldn't
*
* - might be a pathological case where an untyped has a child cap that
* isn't reachable from any of the non root threads. This would result
* in an object being mentioned but never properly defined
*/
print_cap(nextPtr->cap);
}
}
printf("}\n");
}
void obj_cnode_print_attrs(cap_t cnode)
{
printf("(%lu bits)\n", (long unsigned int)cap_cnode_cap_get_capCNodeRadix(cnode));
}
void obj_tcb_print_cnodes(cap_t cnode, tcb_t *tcb)
{
if (seen(cnode)) {
return;
}
add_to_seen(cnode);
printf("%p_cnode = cnode ", (void *)cap_cnode_cap_get_capCNodePtr(cnode));
obj_cnode_print_attrs(cnode);
word_t radix = cap_cnode_cap_get_capCNodeRadix(cnode);
for (uint32_t i = 0; i < (1 << radix); i++) {
lookupCapAndSlot_ret_t c = lookupCapAndSlot(tcb, i);
if (cap_get_capType(c.cap) == cap_untyped_cap) {
/* we need `cte_t *` to print out the slots of an untyped object */
obj_ut_print_attrs(c.slot, tcb);
} else if (cap_get_capType(c.cap) == cap_cnode_cap) {
/* TBD: deal with nested cnodes */
} else if (cap_get_capType(c.cap) != cap_null_cap) {
print_object(c.cap);
}
}
}
/*
* Caps
*/
void cap_cnode_print_attrs(cap_t cnode)
{
printf("(guard: %lu, guard_size: %lu)\n",
(long unsigned int)cap_cnode_cap_get_capCNodeGuard(cnode),
(long unsigned int)cap_cnode_cap_get_capCNodeGuardSize(cnode));
}
void cap_ep_print_attrs(cap_t ep)
{
printf("(");
cap_endpoint_cap_get_capCanReceive(ep) ? putchar('R') : 0;
cap_endpoint_cap_get_capCanSend(ep) ? putchar('W') : 0;
cap_endpoint_cap_get_capCanGrant(ep) ? putchar('G') : 0;
cap_endpoint_cap_get_capCanGrantReply(ep) ? putchar('P') : 0;
long unsigned int badge = cap_endpoint_cap_get_capEPBadge(ep);
badge ? printf(", badge: %lu)\n", badge) : printf(")\n");
}
void cap_ntfn_print_attrs(cap_t ntfn)
{
printf("(");
cap_notification_cap_get_capNtfnCanReceive(ntfn) ? putchar('R') : 0;
cap_notification_cap_get_capNtfnCanSend(ntfn) ? putchar('W') : 0;
long unsigned int badge = cap_notification_cap_get_capNtfnBadge(ntfn);
badge ? printf(", badge: %lu)\n", badge) : printf(")\n");
}
/*
* print object slots
*/
void obj_tcb_print_slots(tcb_t *tcb)
{
printf("%p_tcb {\n", tcb);
/* CSpace root */
if (cap_get_capType(TCB_PTR_CTE_PTR(tcb, tcbCTable)->cap) != cap_null_cap) {
printf("cspace: %p_cnode ",
(void *)cap_cnode_cap_get_capCNodePtr(TCB_PTR_CTE_PTR(tcb, tcbCTable)->cap));
cap_cnode_print_attrs(TCB_PTR_CTE_PTR(tcb, tcbCTable)->cap);
}
/* VSpace root */
if (cap_get_capType(TCB_PTR_CTE_PTR(tcb, tcbVTable)->cap) != cap_null_cap) {
printf("vspace: %p_pd\n",
cap_vtable_cap_get_vspace_root_fp(TCB_PTR_CTE_PTR(tcb, tcbVTable)->cap));
}
/* IPC buffer cap slot */
if (cap_get_capType(TCB_PTR_CTE_PTR(tcb, tcbBuffer)->cap) != cap_null_cap) {
/* TBD: print out the bound vcpu */
print_ipc_buffer_slot(tcb);
}
#ifdef CONFIG_KERNEL_MCS
/* Fault endpoint slot */
if (cap_get_capType(TCB_PTR_CTE_PTR(tcb, tcbFaultHandler)->cap) != cap_null_cap) {
printf("fault_ep_slot: %p_ep ",
(void *)cap_endpoint_cap_get_capEPPtr(TCB_PTR_CTE_PTR(tcb, tcbFaultHandler)->cap));
cap_ep_print_attrs(TCB_PTR_CTE_PTR(tcb, tcbFaultHandler)->cap);
}
/* sc */
if (tcb->tcbSchedContext) {
printf("sc_slot: %p_sc\n", tcb->tcbSchedContext);
}
/* Timeout endpoint slot */
if (cap_get_capType(TCB_PTR_CTE_PTR(tcb, tcbTimeoutHandler)->cap) != cap_null_cap) {
printf("temp_fault_ep_slot: %p_ep ",
(void *)cap_endpoint_cap_get_capEPPtr(TCB_PTR_CTE_PTR(tcb, tcbTimeoutHandler)->cap));
cap_ep_print_attrs(TCB_PTR_CTE_PTR(tcb, tcbTimeoutHandler)->cap);
}
# else
/* Reply cap slot */
if (cap_get_capType(TCB_PTR_CTE_PTR(tcb, tcbReply)->cap) != cap_null_cap) {
printf("reply_slot: %p_reply\n",
(void *)cap_reply_cap_get_capTCBPtr(TCB_PTR_CTE_PTR(tcb, tcbReply)->cap));
}
/* TCB of most recent IPC sender */
if (cap_get_capType(TCB_PTR_CTE_PTR(tcb, tcbCaller)->cap) != cap_null_cap) {
tcb_t *caller = TCB_PTR(cap_thread_cap_get_capTCBPtr(TCB_PTR_CTE_PTR(tcb, tcbCaller)->cap));
printf("caller_slot: %p_tcb\n", caller);
}
#endif /* CONFIG_KERNEL_MCS */
printf("}\n");
}
/* TBD: deal with nested cnodes */
void obj_cnode_print_slots(tcb_t *tcb)
{
cap_t root = TCB_PTR_CTE_PTR(tcb, tcbCTable)->cap;
if (cap_get_capType(root) != cap_cnode_cap) {
return;
}
word_t radix = cap_cnode_cap_get_capCNodeRadix(root);
if (seen(root)) {
return;
}
add_to_seen(root);
printf("%p_cnode {\n", (void *)cap_cnode_cap_get_capCNodePtr(root));
for (uint32_t i = 0; i < (1 << radix); i++) {
lookupCapAndSlot_ret_t c = lookupCapAndSlot(tcb, i);
if (cap_get_capType(c.cap) != cap_null_cap) {
printf("0x%x: ", i);
print_cap(c.cap);
}
}
printf("}\n");
for (uint32_t i = 0; i < (1 << radix); i++) {
lookupCapAndSlot_ret_t c = lookupCapAndSlot(tcb, i);
if (cap_get_capType(c.cap) == cap_irq_handler_cap) {
/* TBD: should instead print it from IRQNode */
obj_irq_print_slots(c.cap);
}
}
}
void obj_irq_print_maps(void)
{
printf("irq maps {\n");
for (seL4_Word target = 0; target < CONFIG_MAX_NUM_NODES; target++) {
for (unsigned i = 0; i <= maxIRQ; i++) {
irq_t irq = CORE_IRQ_TO_IRQT(target, i);
if (isIRQActive(irq)) {
cap_t cap = intStateIRQNode[IRQT_TO_IDX(irq)].cap;
if (cap_get_capType(cap) != cap_null_cap) {
printf("%d: 0x%lx_%lu_irq\n",
i,
#if defined(ENABLE_SMP_SUPPORT) && defined(CONFIG_ARCH_ARM)
(long unsigned int)irq.irq,
#else
(long unsigned int)irq,
#endif
(long unsigned int)target);
}
}
}
}
printf("}\n");
}
void obj_irq_print_slots(cap_t irq_cap)
{
irq_t irq = IDX_TO_IRQT(cap_irq_handler_cap_get_capIRQ(irq_cap));
if (isIRQActive(irq)) {
printf("0x%lx_%lu_irq {\n",
#if defined(ENABLE_SMP_SUPPORT) && defined(CONFIG_ARCH_ARM)
(long unsigned int)irq.irq,
#else
(long unsigned int)irq,
#endif
(long unsigned int)IRQT_TO_CORE(irq));
cap_t ntfn_cap = intStateIRQNode[IRQT_TO_IDX(irq)].cap;
if (cap_get_capType(ntfn_cap) != cap_null_cap) {
printf("0x0: ");
print_cap(ntfn_cap);
}
printf("}\n");
}
}
void print_objects(void)
{
for (tcb_t *curr = NODE_STATE(ksDebugTCBs); curr != NULL; curr = TCB_PTR_DEBUG_PTR(curr)->tcbDebugNext) {
if (root_or_idle_tcb(curr)) {
continue;
}
/* print the contains of the tcb's vtable as objects */
obj_tcb_print_vtable(curr);
}
for (tcb_t *curr = NODE_STATE(ksDebugTCBs); curr != NULL; curr = TCB_PTR_DEBUG_PTR(curr)->tcbDebugNext) {
if (root_or_idle_tcb(curr)) {
continue;
}
/* print the tcb as objects */
printf("%p_tcb = tcb ", curr);
obj_tcb_print_attrs(curr);
/* print the contains of the tcb's ctable as objects */
if (cap_get_capType(TCB_PTR_CTE_PTR(curr, tcbCTable)->cap) == cap_cnode_cap) {
obj_tcb_print_cnodes(TCB_PTR_CTE_PTR(curr, tcbCTable)->cap, curr);
}
}
}
void print_caps(void)
{
for (tcb_t *curr = NODE_STATE(ksDebugTCBs); curr != NULL; curr = TCB_PTR_DEBUG_PTR(curr)->tcbDebugNext) {
if (root_or_idle_tcb(curr)) {
continue;
}
obj_cnode_print_slots(curr);
obj_vtable_print_slots(curr);
obj_tcb_print_slots(curr);
}
}
void print_cap(cap_t cap)
{
switch (cap_get_capType(cap)) {
case cap_endpoint_cap: {
printf("%p_ep ",
(void *)cap_endpoint_cap_get_capEPPtr(cap));
cap_ep_print_attrs(cap);
break;
}
case cap_notification_cap: {
printf("%p_notification ",
(void *)cap_notification_cap_get_capNtfnPtr(cap));
cap_ntfn_print_attrs(cap);
break;
}
case cap_untyped_cap: {
printf("%p_untyped\n",
(void *)cap_untyped_cap_get_capPtr(cap));
break;
}
case cap_thread_cap: {
printf("%p_tcb\n",
(void *)cap_thread_cap_get_capTCBPtr(cap));
break;
}
case cap_cnode_cap: {
printf("%p_cnode ",
(void *)cap_cnode_cap_get_capCNodePtr(cap));
cap_cnode_print_attrs(cap);
break;
}
#ifdef CONFIG_KERNEL_MCS
case cap_reply_cap: {
printf("%p_reply\n",
(void *)cap_reply_cap_get_capReplyPtr(cap));
break;
}
case cap_sched_context_cap: {
printf("%p_sc\n",
(void *)cap_sched_context_cap_get_capSCPtr(cap));
break;
}
case cap_sched_control_cap: {
printf("%lu_sched_control\n",
(long unsigned int)cap_sched_control_cap_get_core(cap));
break;
}
#endif
case cap_irq_control_cap: {
printf("irq_control\n"); /* only one in the system */
break;
}
case cap_irq_handler_cap: {
printf("%p_%lu_irq\n",
(void *)cap_irq_handler_cap_get_capIRQ(cap),
(long unsigned int)IRQT_TO_CORE(IDX_TO_IRQT(cap_irq_handler_cap_get_capIRQ(cap))));
break;
}
default: {
print_cap_arch(cap);
break;
}
}
}
void print_object(cap_t cap)
{
switch (cap_get_capType(cap)) {
case cap_endpoint_cap: {
printf("%p_ep = ep\n",
(void *)cap_endpoint_cap_get_capEPPtr(cap));
break;
}
case cap_notification_cap: {
printf("%p_notification = notification\n",
(void *)cap_notification_cap_get_capNtfnPtr(cap));
break;
}
case cap_thread_cap: {
/* this object has already got handle by `print_objects` */
break;
}
case cap_cnode_cap: {
assert(!"should not happend");
}
#ifdef CONFIG_KERNEL_MCS
case cap_reply_cap: {
printf("%p_reply = rtreply\n",
(void *)cap_reply_cap_get_capReplyPtr(cap));
break;
}
case cap_sched_context_cap: {
printf("%p_sc = sc ",
(void *)cap_sched_context_cap_get_capSCPtr(cap));
obj_sc_print_attrs(cap);
break;
}
#endif
case cap_irq_handler_cap: {
printf("%p_%lu_irq = irq\n",
(void *)cap_irq_handler_cap_get_capIRQ(cap),
(long unsigned int)IRQT_TO_CORE(IDX_TO_IRQT(cap_irq_handler_cap_get_capIRQ(cap))));
break;
}
default:
print_object_arch(cap);
break;
}
}
#endif