Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions include/sysinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,33 @@
#define _PH_SYSINFO_H_


/* threadsinfo attributes */
#define PH_THREADINFO_THREADS_ALL (-1)

#define PH_THREADINFO_TID (1UL << 1)
#define PH_THREADINFO_PRIO (1UL << 2)
#define PH_THREADINFO_STATE (1UL << 3)
#define PH_THREADINFO_LOAD (1UL << 4)
#define PH_THREADINFO_CPUTIME (1UL << 5)
#define PH_THREADINFO_WAITING (1UL << 6)
#define PH_THREADINFO_NAME (1UL << 7)
#define PH_THREADINFO_VMEM (1UL << 8)
#define PH_THREADINFO_PPID (1UL << 9)

#define PH_THREADINFO_ALL ( \
PH_THREADINFO_TID | \
PH_THREADINFO_PRIO | \
PH_THREADINFO_STATE | \
PH_THREADINFO_LOAD | \
PH_THREADINFO_CPUTIME | \
PH_THREADINFO_WAITING | \
PH_THREADINFO_NAME | \
PH_THREADINFO_VMEM | \
PH_THREADINFO_PPID)

#define PH_THREADINFO_OPT_THREADCOUNT (1UL << 10)


typedef struct _syspageprog_t {
char name[32];
addr_t addr;
Expand Down
270 changes: 183 additions & 87 deletions proc/threads.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,17 @@ static struct {
intr_handler_t pendsvHandler;
#endif

/* Synchronized by spinlock */
thread_t *ghosts;
thread_t *reaper;

/* Synchronized by spinlock */
int perfGather;
time_t perfLastTimestamp;
cbuffer_t perfBuffer;
page_t *perfPages;

/* Debug */
/* Debug, synchronized by spinlock */
unsigned char stackCanary[16];
time_t prev;
} threads_common;
Expand Down Expand Up @@ -2086,120 +2088,214 @@ void proc_threadsDump(u8 priority)
}


int proc_threadsList(int n, threadinfo_t *info)
static inline void _proc_makeProcessName(thread_t *thread, char *name, size_t sz)
{
int i = 0, argc;
unsigned int len, space;
thread_t *t;
int i, len;
long left = sz;

if (thread == NULL || name == NULL || sz == 0) {
return;
}

hal_memset(name, 0, sz);
if (thread->process != NULL && thread->process->argv != NULL) {
for (i = 0; thread->process->argv[i] != NULL; i++) {
if (left <= 0) {
break;
}
len = min(hal_strlen(thread->process->argv[i]) + 1U, left);
hal_memcpy(name, thread->process->argv[i], len);
name[len - 1U] = ' ';
name += len;
left -= len;
}
*(name - 1) = '\0';
}
else if (thread->process != NULL && thread->process->path != NULL) {
len = min(sz, hal_strlen(thread->process->path) + 1U);
hal_memcpy(name, thread->process->path, len);
name[len - 1] = '\0';
}
else if (thread->process == NULL) {
hal_memcpy(name, "[idle]", sizeof("[idle]"));
}
else {
}
}


static inline int _proc_calculateVmem(thread_t *thread)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefixed with _ - does it require any external sync? there is a spinlock inside, so I guess not?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It uses a thread_t *, so we have to guarantee that it won't be deallocated, therefore it should be called with threads_common.lock set, as all the other functions here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would make it explicit in the comment then, as the use of thread_t * alone doesn't imply that - usually you just up the refcnt on a thread, guaranteeing that it won't get deallocated when you need it and threadsinfo is an exception.

{
int vmem = 0;
map_entry_t *entry;

#ifdef NOMMU
if (thread->process != NULL) {
entry = thread->process->entries;
if (entry != NULL) {
do {
vmem += (int)entry->size;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ik this would break the interface, but is there a reason [threadinfo_t]::vmem is int? if not - could it be changed to unsigned (or size_t) to avoid these awkward casts?

After a quick look: this shouldn't break much, at least in the main repo

Copy link
Member

@adamgreloch adamgreloch Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 - these changes are already breaking, so IMO this is a good opportunity to tidy up types in threadinfo_t and potentially reduce casts

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so another thing if this is breaking: signal it with ! in PR title and checkbox in description

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm dropping the vmem type change, correcting those type may have bigger implications and should be considered on its own.

entry = entry->next;
} while (entry != thread->process->entries);
}
}
else {
}
#else /* !NOMMU */
vm_map_t *map;
time_t now;
char *name;
spinlock_ctx_t sc;

(void)proc_lockSet(&threads_common.lock);
hal_spinlockSet(&threads_common.spinlock, &sc);

t = lib_treeof(thread_t, idlinkage, lib_rbMinimum(threads_common.id.root));
if (thread->process != NULL) {
map = thread->process->mapp;
}
else {
map = threads_common.kmap;
}

while (i < n && t != NULL) {
if (t->process != NULL) {
info[i].pid = process_getPid(t->process);
/* TODO: info[i].ppid = t->process->parent != NULL ? t->process->parent->id : 0; */
info[i].ppid = 0;
}
else {
info[i].pid = 0;
info[i].ppid = 0;
hal_spinlockClear(&threads_common.spinlock, &sc);

if (map != NULL) {
(void)proc_lockSet(&map->lock);

entry = lib_treeof(map_entry_t, linkage, lib_rbMinimum(map->tree.root));
while (entry != NULL) {
vmem += (int)entry->size;
entry = lib_treeof(map_entry_t, linkage, lib_rbNext(&entry->linkage));
}

hal_spinlockSet(&threads_common.spinlock, &sc);
info[i].tid = (unsigned int)proc_getTid(t);
info[i].priority = (int)t->priorityBase;
info[i].state = (int)t->state;
(void)proc_lockClear(&map->lock);
}
else {
}
#endif

now = _proc_gettimeRaw();
if (now != t->startTime) {
info[i].load = (int)((t->cpuTime * 1000) / (now - t->startTime));
return vmem;
}


/* call with threads_common.lock set */
void _proc_threadInfo(thread_t *thread, unsigned int flags, threadinfo_t *info)
{
time_t now;
pid_t ppid;

if (thread == NULL || info == NULL) {
return;
}

now = _proc_gettimeRaw();
if (thread->process != NULL) {
info->pid = process_getPid(thread->process);
info->ppid = 0;
}
else {
info->pid = 0;
info->ppid = 0;
}

if ((flags & PH_THREADINFO_TID) != 0) {
info->tid = (unsigned int)proc_getTid(thread);
}
else {
}

if ((flags & PH_THREADINFO_PRIO) != 0) {
info->priority = (int)thread->priorityBase;
}
else {
}

if ((flags & PH_THREADINFO_STATE) != 0) {
info->state = (int)thread->state;
}
else {
}

if ((flags & PH_THREADINFO_LOAD) != 0) {
if (now != thread->startTime) {
info->load = (int)((thread->cpuTime * 1000) / (now - thread->startTime));
}
else {
info[i].load = 0;
info->load = 0;
}
info[i].cpuTime = t->cpuTime;
}

if (t->state == READY && t->maxWait < now - t->readyTime) {
info[i].wait = now - t->readyTime;
if ((flags & PH_THREADINFO_WAITING) != 0) {
if (thread->state == READY && thread->maxWait < now - thread->readyTime) {
info->wait = now - thread->readyTime;
}
else {
info[i].wait = t->maxWait;
info->wait = thread->maxWait;
}
hal_spinlockClear(&threads_common.spinlock, &sc);
}
else {
}

if (t->process != NULL) {
map = t->process->mapp;

if (t->process->path != NULL) {
space = sizeof(info[i].name);
name = info[i].name;

if (t->process->argv != NULL) {
for (argc = 0; t->process->argv[argc] != NULL; ++argc) {
if ((int)space <= 0) {
break;
}
len = min(hal_strlen(t->process->argv[argc]) + 1U, space);
hal_memcpy(name, t->process->argv[argc], len);
name[len - 1U] = ' ';
name += len;
space -= len;
}
*(name - 1) = '\0';
}
else {
len = hal_strlen(t->process->path) + 1U;
hal_memcpy(info[i].name, t->process->path, min(space, len));
}
if ((flags & PH_THREADINFO_CPUTIME) != 0) {
info->cpuTime = thread->cpuTime;
}
else {
}

info[i].name[sizeof(info[i].name) - 1U] = '\0';
}
else {
info[i].name[0] = '\0';
}
if ((flags & PH_THREADINFO_NAME) != 0) {
_proc_makeProcessName(thread, info->name, sizeof(info->name));
}
else {
}

if ((flags & PH_THREADINFO_VMEM) != 0) {
info->vmem = _proc_calculateVmem(thread);
}
else {
}

if ((flags & PH_THREADINFO_PPID) != 0) {
ppid = posix_getppid(info->pid);
if (ppid > 0) {
info->ppid = ppid;
}
else {
map = threads_common.kmap;
hal_memcpy(info[i].name, "[idle]", sizeof("[idle]"));
}
}
else {
}
}

info[i].vmem = 0;

#ifdef NOMMU
if (t->process != NULL) {
entry = t->process->entries;
if (entry != NULL) {
do {
info[i].vmem += (int)entry->size;
entry = entry->next;
} while (entry != t->process->entries);
}
}
else
#endif
if (map != NULL) {
(void)proc_lockSet(&map->lock);
entry = lib_treeof(map_entry_t, linkage, lib_rbMinimum(map->tree.root));
int proc_threadsInfo(int tid, unsigned int flags, int n, threadinfo_t *info)
{
int i = 0;
thread_t *t;

while (entry != NULL) {
info[i].vmem += (int)entry->size;
entry = lib_treeof(map_entry_t, linkage, lib_rbNext(&entry->linkage));
(void)proc_lockSet(&threads_common.lock);

if (tid == PH_THREADINFO_THREADS_ALL) {
t = lib_treeof(thread_t, idlinkage, lib_rbMinimum(threads_common.id.root));

do {
if ((flags & PH_THREADINFO_OPT_THREADCOUNT) == 0) {
if (i >= n) {
break;
}

_proc_threadInfo(t, flags, &info[i]);
}
(void)proc_lockClear(&map->lock);
}
else {
/* No action required */

t = lib_idtreeof(thread_t, idlinkage, lib_idtreeNext(&t->idlinkage.linkage));
i++;
} while (t != NULL);
}
else {
t = lib_idtreeof(thread_t, idlinkage, lib_idtreeFind(&threads_common.id, tid));
if (t == NULL) {
(void)proc_lockClear(&threads_common.lock);
return -ENOENT;
}

++i;
t = lib_idtreeof(thread_t, idlinkage, lib_idtreeNext(&t->idlinkage.linkage));
_proc_threadInfo(t, flags, info);
i++;
}

(void)proc_lockClear(&threads_common.lock);
Expand Down
2 changes: 1 addition & 1 deletion proc/threads.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ int proc_join(int tid, time_t timeout);
void proc_changeMap(process_t *proc, vm_map_t *map, vm_map_t *imap, pmap_t *pmap);


int proc_threadsList(int n, threadinfo_t *info);
int proc_threadsInfo(int tid, unsigned int flags, int n, threadinfo_t *info);


int proc_threadsOther(thread_t *t);
Expand Down
21 changes: 7 additions & 14 deletions syscalls.c
Original file line number Diff line number Diff line change
Expand Up @@ -380,27 +380,20 @@ int syscalls_priority(u8 *ustack)
int syscalls_threadsinfo(u8 *ustack)
{
process_t *proc = proc_current()->process;
int n, i;
pid_t ppid;
int n, tid;
unsigned int flags;
threadinfo_t *info;

GETFROMSTACK(ustack, int, n, 0);
GETFROMSTACK(ustack, threadinfo_t *, info, 1);
GETFROMSTACK(ustack, int, tid, 0);
GETFROMSTACK(ustack, unsigned int, flags, 1);
GETFROMSTACK(ustack, int, n, 2);
GETFROMSTACK(ustack, threadinfo_t *, info, 3);

if (vm_mapBelongs(proc, info, sizeof(*info) * (size_t)n) < 0) {
return -EFAULT;
}

n = proc_threadsList(n, info);

for (i = 0; i < n; ++i) {
ppid = posix_getppid(info[i].pid);
if (ppid > 0) {
info[i].ppid = ppid;
}
}

return n;
return proc_threadsInfo(tid, flags, n, info);
}


Expand Down
Loading