blob: ab09e9697f32916afed695d2f17d4f53cee8da2b [file] [log] [blame] [raw]
/*
htop - haiku/HaikuProcessList.c
(C) 2014 Hisham H. Muhammad
Copyright 2015-2022 Rivoreo
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
/*{
#include "ProcessList.h"
#include <SupportDefs.h>
struct cpu_data {
bigtime_t time;
bigtime_t period;
};
typedef struct {
ProcessList super;
int team_count;
struct cpu_data *cpu_data;
bigtime_t last_updated;
bigtime_t interval;
} HaikuProcessList;
}*/
#define _XOPEN_SOURCE 600
#include "config.h"
#include "HaikuProcessList.h"
#include "HaikuProcess.h"
#include "StringUtils.h"
#include "Settings.h"
#include "Platform.h"
#include "CRT.h"
#include <OS.h>
#include <dlfcn.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define KMESSAGE_BUFFER_ALIGNMENT 4
#define KMESSAGE_HEADER_MAGIC 0x6b4d7347
#define KMESSAGE_ALIGN(P,O) ((void *)(((uintptr_t)(P)+(O)+KMESSAGE_BUFFER_ALIGNMENT-1) & ~(KMESSAGE_BUFFER_ALIGNMENT-1)))
struct field_header {
type_code type;
int32_t element_size;
int32_t element_count;
int32_t field_size;
int16_t header_size;
char name[0];
};
struct field_value_header {
int32_t size;
};
struct kmessage_header {
uint32_t magic;
int32_t size;
uint32_t what;
team_id sender;
int32_t target_token;
port_id reply_port;
int32_t reply_token;
};
ProcessList* ProcessList_new(UsersTable* usersTable, const Hashtable *pidWhiteList, uid_t userId) {
HaikuProcessList *this = xCalloc(1, sizeof(HaikuProcessList));
ProcessList_init(&this->super, Class(HaikuProcess), usersTable, pidWhiteList, userId);
system_info si;
get_system_info(&si);
this->cpu_data = xCalloc(si.cpu_count, sizeof(struct cpu_data));
this->super.cpuCount = si.cpu_count;
this->super.totalMem = si.max_pages * (B_PAGE_SIZE / 1024);
return &this->super;
}
void ProcessList_delete(ProcessList* this) {
ProcessList_done(this);
free(((HaikuProcessList *)this)->cpu_data);
free(this);
}
static const char state_name_map[] = {
[B_THREAD_RUNNING] = 'O',
[B_THREAD_READY] = 'R',
[B_THREAD_RECEIVING] = 'D',
[B_THREAD_ASLEEP] = 'S',
[B_THREAD_SUSPENDED] = 'T',
[B_THREAD_WAITING] = 'S'
};
static void get_extended_team_info(pid_t team_id, uid_t *ruid, uid_t *euid, pid_t *pgrp, pid_t *session) {
*ruid = -1;
*euid = -1;
*pgrp = -1;
*session = -1;
static bool is_available = true;
if(!is_available) return;
static status_t (*get_extended_team_info_p)(int32_t, uint32_t, void *, size_t, size_t *);
if(!get_extended_team_info_p) {
void *libroot = get_libroot();
if(!libroot) {
is_available = false;
return;
}
*(void **)&get_extended_team_info_p = dlsym(libroot, "_kern_get_extended_team_info");
if(!get_extended_team_info_p) {
is_available = false;
return;
}
}
size_t size;
status_t status = get_extended_team_info_p(team_id, 1, NULL, 0, &size);
if(status != B_OK && status != B_BUFFER_OVERFLOW) return;
if(size < sizeof(struct kmessage_header)) return;
char buffer[size];
status = get_extended_team_info_p(team_id, 1, buffer, size, &size);
if(status != B_OK) return;
if(size < sizeof(struct kmessage_header)) return;
const struct kmessage_header *header = (const struct kmessage_header *)buffer;
if(header->magic != KMESSAGE_HEADER_MAGIC) return;
if((size_t)header->size != size) return;
struct field_header *field_header = KMESSAGE_ALIGN(buffer, sizeof(struct kmessage_header));
while((char *)field_header < buffer + size) {
if(field_header->element_size == 4) {
char *value_p = (char *)field_header + field_header->header_size;
int32_t value;
if(strcmp(field_header->name, "process group") == 0) {
memcpy(&value, value_p, 4);
*pgrp = value;
} else if(strcmp(field_header->name, "session") == 0) {
memcpy(&value, value_p, 4);
*session = value;
} else if(strcmp(field_header->name, "uid") == 0) {
memcpy(&value, value_p, 4);
*ruid = value;
} else if(strcmp(field_header->name, "euid") == 0) {
memcpy(&value, value_p, 4);
*euid = value;
}
}
field_header = KMESSAGE_ALIGN(field_header, field_header->field_size);
}
}
static pid_t get_parent_team_id(pid_t team_id) {
// XXX: This system call works only for the calling team
static bool is_available = true;
if(!is_available) return -1;
static pid_t (*get_id)(pid_t, int32_t);
if(!get_id) {
void *libroot = get_libroot();
if(!libroot) {
is_available = false;
return -1;
}
*(void **)&get_id = dlsym(libroot, "_kern_process_info");
if(!get_id) {
is_available = false;
return -1;
}
}
return get_id(team_id, 3);
}
void ProcessList_goThroughEntries(ProcessList *super) {
HaikuProcessList *this = (HaikuProcessList *)super;
bigtime_t now = system_time();
this->interval = now - this->last_updated;
this->last_updated = now;
system_info si;
get_system_info(&si);
#ifdef HAVE_SYSTEM_INFO_CPU_INFOS
#define CPU_INFOS si.cpu_infos
#elif defined HAVE_GET_CPU_INFO
cpu_info cpu_infos[si.cpu_count];
get_cpu_info(0, si.cpu_count, cpu_infos);
#define CPU_INFOS cpu_infos
#endif
for(int i = 0; i < super->cpuCount; i++) {
struct cpu_data *data = this->cpu_data + i;
if(
#ifdef HAVE_CPU_INFO_ENABLED
CPU_INFOS[i].enabled &&
#endif
i < (int)si.cpu_count) {
bigtime_t current_time = CPU_INFOS[i].active_time;
data->period = current_time > data->time ? current_time - data->time : 0;
data->time = current_time;
} else {
data->period = 0;
}
}
super->usedMem = si.used_pages * CRT_page_size_kib;
#if !defined HAVE_SYSTEM_INFO_CACHED_PAGES || !defined HAVE_SYSTEM_INFO_MAX_SWAP_PAGES
struct vm_stat vm_stat;
if(Platform_getVMStat(&vm_stat, sizeof vm_stat)) {
// Is this correct?
long int cache_page_count =
si.max_pages - (si.used_pages + vm_stat.free_memory / CRT_page_size);
super->cachedMem = cache_page_count > 0 ? cache_page_count * CRT_page_size_kib : 0;
super->totalSwap = vm_stat.max_swap_space / 1024;
super->usedSwap = (vm_stat.max_swap_space - vm_stat.free_swap_space) / 1024;
} else {
super->cachedMem = 0;
}
#endif
#ifdef HAVE_SYSTEM_INFO_CACHED_PAGES
super->cachedMem = si.cached_pages * CRT_page_size_kib;
#endif
#ifdef HAVE_SYSTEM_INFO_MAX_SWAP_PAGES
super->totalSwap = si.max_swap_pages * CRT_page_size_kib;
super->usedSwap = (si.max_swap_pages - si.free_swap_pages) * CRT_page_size_kib;
#endif
this->team_count = 0;
int32 team_cookie = 0;
team_info team_info;
while(get_next_team_info(&team_cookie, &team_info) == B_OK) {
long int *nthr_p = NULL;
uid_t ruid, euid;
pid_t pgrp, session;
get_extended_team_info(team_info.team, &ruid, &euid, &pgrp, &session);
if(ruid == (uid_t)-1) ruid = team_info.uid;
if(euid == (uid_t)-1) euid = team_info.uid;
int32 thread_cookie = 0;
thread_info thread_info;
while(get_next_thread_info(team_info.team, &thread_cookie, &thread_info) == B_OK) {
bool is_existing;
Process *proc =
ProcessList_getProcess(super, thread_info.thread, &is_existing,
(Process_New)HaikuProcess_new);
HaikuProcess *haiku_proc = (HaikuProcess *)proc;
if(is_existing) {
if(proc->ruid != ruid) proc->real_user = NULL;
if(proc->euid != euid) proc->effective_user = NULL;
}
proc->state = thread_info.state < sizeof state_name_map ?
state_name_map[thread_info.state] : '?';
proc->ruid = ruid;
proc->euid = euid;
proc->ppid = get_parent_team_id(team_info.team);
proc->pgrp = pgrp;
proc->session = session;
proc->priority = thread_info.priority;
#if defined NZERO && defined HAVE_SETPRIORITY
// From Haiku src/system/libroot/posix/sys/priority.c
#define BZERO B_NORMAL_PRIORITY
#define BMAX (B_REAL_TIME_DISPLAY_PRIORITY - 1)
#define BMIN 1
proc->nice = thread_info.priority < BZERO ?
((BZERO - thread_info.priority) * (NZERO - 1)) / (BZERO - BMIN) + 1 :
-(((thread_info.priority - BZERO) * NZERO + (BMAX - BZERO) / 2) / (BMAX - BZERO));
#else
proc->nice = B_NORMAL_PRIORITY - thread_info.priority;
#endif
bigtime_t time = thread_info.user_time + thread_info.kernel_time;
proc->time = time / 10000;
double delta = time > haiku_proc->time_usec ?
time - haiku_proc->time_usec : 0;
proc->percent_cpu = delta / (double)this->interval * 100;
haiku_proc->time_usec = time;
if(!proc->real_user) {
proc->real_user = UsersTable_getRef(super->usersTable, proc->ruid);
}
if(!proc->effective_user) {
proc->effective_user = UsersTable_getRef(super->usersTable, proc->euid);
}
if(!is_existing) {
proc->tgid = team_info.team;
ProcessList_add(super, proc);
}
if(!is_existing || super->settings->updateProcessNames) {
free(proc->name);
free(proc->comm);
proc->name = xStrdup(thread_info.name);
proc->comm = xStrdup(team_info.args);
}
super->totalTasks++;
super->thread_count++;
if(Process_isKernelProcess(proc)) {
super->kernel_process_count++;
super->kernel_thread_count++;
}
if(proc->state == 'O') {
super->running_process_count++;
super->running_thread_count++;
}
proc->show = (!((super->settings->hide_kernel_processes && Process_isKernelProcess(proc)) ||
(super->settings->hide_thread_processes && Process_isExtraThreadProcess(proc))));
proc->updated = true;
if(!Process_isExtraThreadProcess(proc)) nthr_p = &proc->nlwp;
}
if(nthr_p) *nthr_p = team_info.thread_count;
this->team_count++;
}
}