summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2017-05-31 21:12:41 +0200
committerRichard Braun <rbraun@sceen.net>2017-05-31 21:12:41 +0200
commit60d1eb47786f9b2461067f65f62fde31d34f1089 (patch)
tree578bad3cd9a20dc58ae9539112c92a81d07ff55c
parentccefd921e76ba4ca7f3d6b54691315c0718b039c (diff)
kern/{task,thread}: add the task_info and thread_trace shell commands
-rw-r--r--kern/task.c89
-rw-r--r--kern/task.h42
-rw-r--r--kern/thread.c59
3 files changed, 189 insertions, 1 deletions
diff --git a/kern/task.c b/kern/task.c
index 7f1c53d2..66b43bff 100644
--- a/kern/task.c
+++ b/kern/task.c
@@ -24,6 +24,7 @@
#include <kern/kmem.h>
#include <kern/list.h>
#include <kern/param.h>
+#include <kern/shell.h>
#include <kern/spinlock.h>
#include <kern/task.h>
#include <kern/thread.h>
@@ -56,20 +57,61 @@ static struct spinlock task_list_lock;
static void
task_init(struct task *task, const char *name, struct vm_map *map)
{
+ task->nr_refs = 1;
spinlock_init(&task->lock);
list_init(&task->threads);
task->map = map;
strlcpy(task->name, name, sizeof(task->name));
}
+static void
+task_shell_info(int argc, char *argv[])
+{
+ struct task *task;
+ int error;
+
+ if (argc == 1) {
+ task_info(NULL);
+ return;
+ } else {
+ task = task_lookup(argv[1]);
+
+ if (task == NULL) {
+ error = ERROR_INVAL;
+ goto error;
+ }
+
+ task_info(task);
+ task_unref(task);
+ }
+
+ return;
+
+error:
+ printf("task: info: %s\n", error_str(error));
+}
+
+static struct shell_cmd task_shell_cmds[] = {
+ SHELL_CMD_INITIALIZER("task_info", task_shell_info,
+ "task_info", "print all tasks and threads"),
+};
+
void __init
task_setup(void)
{
+ unsigned int i;
+ int error;
+
kmem_cache_init(&task_cache, "task", sizeof(struct task), 0, NULL, 0);
list_init(&task_list);
spinlock_init(&task_list_lock);
task_init(kernel_task, "x15", kernel_map);
list_insert_head(&task_list, &kernel_task->node);
+
+ for (i = 0; i < ARRAY_SIZE(task_shell_cmds); i++) {
+ error = shell_cmd_register(&task_shell_cmds[i]);
+ error_check(error, __func__);
+ }
}
int
@@ -107,6 +149,31 @@ error_task:
return error;
}
+struct task *
+task_lookup(const char *name)
+{
+ struct task *task;
+
+ spinlock_lock(&task_list_lock);
+
+ list_for_each_entry(&task_list, task, node) {
+ spinlock_lock(&task->lock);
+
+ if (strcmp(task->name, name) == 0) {
+ task_ref(task);
+ spinlock_unlock(&task->lock);
+ spinlock_unlock(&task_list_lock);
+ return task;
+ }
+
+ spinlock_unlock(&task->lock);
+ }
+
+ spinlock_unlock(&task_list_lock);
+
+ return NULL;
+}
+
void
task_add_thread(struct task *task, struct thread *thread)
{
@@ -123,6 +190,26 @@ task_remove_thread(struct task *task, struct thread *thread)
spinlock_unlock(&task->lock);
}
+struct thread *
+task_lookup_thread(struct task *task, const char *name)
+{
+ struct thread *thread;
+
+ spinlock_lock(&task->lock);
+
+ list_for_each_entry(&task->threads, thread, task_node) {
+ if (strcmp(thread->name, name) == 0) {
+ thread_ref(thread);
+ spinlock_unlock(&task->lock);
+ return thread;
+ }
+ }
+
+ spinlock_unlock(&task->lock);
+
+ return NULL;
+}
+
void
task_info(struct task *task)
{
@@ -149,6 +236,8 @@ task_info(struct task *task)
* can be used to debug in the middle of most critical sections.
* Threads are only destroyed after being removed from their task
* so holding the task lock is enough to guarantee existence.
+ *
+ * TODO Handle tasks and threads names modifications.
*/
list_for_each_entry(&task->threads, thread, task_node) {
printf(TASK_INFO_ADDR_FMT " %c %8s:" TASK_INFO_ADDR_FMT
diff --git a/kern/task.h b/kern/task.h
index 67599399..cfe694bf 100644
--- a/kern/task.h
+++ b/kern/task.h
@@ -18,6 +18,7 @@
#ifndef _KERN_TASK_H
#define _KERN_TASK_H
+#include <kern/atomic.h>
#include <kern/list.h>
#include <kern/spinlock.h>
#include <kern/thread.h>
@@ -32,6 +33,7 @@
* Task structure.
*/
struct task {
+ unsigned long nr_refs;
struct spinlock lock;
struct list node;
struct list threads;
@@ -44,6 +46,28 @@ struct task {
*/
extern struct task *kernel_task;
+static inline void
+task_ref(struct task *task)
+{
+ unsigned long nr_refs;
+
+ nr_refs = atomic_fetch_add(&task->nr_refs, 1, ATOMIC_RELAXED);
+ assert(nr_refs != (unsigned long)-1);
+}
+
+static inline void
+task_unref(struct task *task)
+{
+ unsigned long nr_refs;
+
+ nr_refs = atomic_fetch_sub_acq_rel(&task->nr_refs, 1);
+ assert(nr_refs != 0);
+
+ if (nr_refs == 1) {
+ /* TODO Task destruction */
+ }
+}
+
/*
* Initialize the task module.
*/
@@ -55,6 +79,15 @@ void task_setup(void);
int task_create(struct task **taskp, const char *name);
/*
+ * Look up a task from its name.
+ *
+ * If a task is found, it gains a reference. Otherwise, NULL is returned.
+ *
+ * This function is meant for debugging only.
+ */
+struct task * task_lookup(const char *name);
+
+/*
* Add a thread to a task.
*/
void task_add_thread(struct task *task, struct thread *thread);
@@ -65,6 +98,15 @@ void task_add_thread(struct task *task, struct thread *thread);
void task_remove_thread(struct task *task, struct thread *thread);
/*
+ * Look up a thread in a task from its name.
+ *
+ * If a thread is found, it gains a reference, Otherwise, NULL is returned.
+ *
+ * This function is meant for debugging only.
+ */
+struct thread * task_lookup_thread(struct task *task, const char *name);
+
+/*
* Display task information.
*
* If task is NULL, this function displays all tasks.
diff --git a/kern/thread.c b/kern/thread.c
index 68aa7285..17ac10bd 100644
--- a/kern/thread.c
+++ b/kern/thread.c
@@ -101,6 +101,7 @@
#include <kern/panic.h>
#include <kern/param.h>
#include <kern/percpu.h>
+#include <kern/shell.h>
#include <kern/sleepq.h>
#include <kern/spinlock.h>
#include <kern/sref.h>
@@ -2228,10 +2229,61 @@ thread_setup_runq(struct thread_runq *runq)
thread_setup_idler(runq);
}
+/*
+ * This function is meant for debugging only. As a result, it uses a weak
+ * locking policy which allows tracing threads which state may mutate during
+ * tracing.
+ */
+static void
+thread_shell_trace(int argc, char *argv[])
+{
+ const char *task_name, *thread_name;
+ struct thread *thread;
+ struct task *task;
+ int error;
+
+ if (argc != 3) {
+ error = ERROR_INVAL;
+ goto error;
+ }
+
+ task_name = argv[1];
+ thread_name = argv[2];
+
+ task = task_lookup(task_name);
+
+ if (task == NULL) {
+ error = ERROR_SRCH;
+ goto error;
+ }
+
+ thread = task_lookup_thread(task, thread_name);
+ task_unref(task);
+
+ if (thread == NULL) {
+ error = ERROR_SRCH;
+ goto error;
+ }
+
+ tcb_trace(&thread->tcb);
+ thread_unref(thread);
+ return;
+
+error:
+ printf("thread: trace: %s\n", error_str(error));
+}
+
+static struct shell_cmd thread_shell_cmds[] = {
+ SHELL_CMD_INITIALIZER("thread_trace", thread_shell_trace,
+ "thread_trace <task_name> <thread_name>",
+ "print the stack trace of a given thread"),
+};
+
void __init
thread_setup(void)
{
- int cpu;
+ unsigned int i;
+ int cpu, error;
for (cpu = 1; (unsigned int)cpu < cpu_count(); cpu++) {
thread_bootstrap_common(cpu);
@@ -2249,6 +2301,11 @@ thread_setup(void)
cpumap_for_each(&thread_active_runqs, cpu) {
thread_setup_runq(percpu_ptr(thread_runq, cpu));
}
+
+ for (i = 0; i < ARRAY_SIZE(thread_shell_cmds); i++) {
+ error = shell_cmd_register(&thread_shell_cmds[i]);
+ error_check(error, __func__);
+ }
}
int