From af560c6566600a85282153ac1d04b4879abe5ce9 Mon Sep 17 00:00:00 2001 From: Alastair Poole Date: Wed, 23 Sep 2020 19:57:24 +0100 Subject: [PATCH] procstats: window resource module. The module sends messages which the theme displays. Thus, the module requires a theme with an implemented edj script to be useful. Currently EFL 1.26 includes a basic text label for this... Usage is recursive...everything under the window is included. Rudimentary, as usual...please do something pretty... RFC. Hopefully I didn't break the universe...again! --- meson_options.txt | 4 + src/bin/e_client.h | 1 + src/bin/e_comp_object.c | 2 +- src/modules/meson.build | 1 + src/modules/procstats/e-module-procstats.edj | Bin 0 -> 12522 bytes src/modules/procstats/e_mod_main.c | 233 +++++ src/modules/procstats/meson.build | 12 + src/modules/procstats/module.desktop | 6 + src/modules/procstats/process.c | 970 +++++++++++++++++++ src/modules/procstats/process.h | 65 ++ 10 files changed, 1293 insertions(+), 1 deletion(-) create mode 100644 src/modules/procstats/e-module-procstats.edj create mode 100644 src/modules/procstats/e_mod_main.c create mode 100644 src/modules/procstats/meson.build create mode 100644 src/modules/procstats/module.desktop create mode 100644 src/modules/procstats/process.c create mode 100644 src/modules/procstats/process.h diff --git a/meson_options.txt b/meson_options.txt index faedfeada..64f0be347 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -301,3 +301,7 @@ option('gesture-recognition', type: 'boolean', value: true, description: 'Enable gesture recognition using libinput, needed to get swipe bindings beeing detected.') +option('procstats', + type: 'boolean', + value: true, + description: 'enable procstats module: (default=true)') diff --git a/src/bin/e_client.h b/src/bin/e_client.h index 6c178819c..4725ba53c 100644 --- a/src/bin/e_client.h +++ b/src/bin/e_client.h @@ -236,6 +236,7 @@ struct E_Client } pre_cb; Eina_Rectangle client; //client geom Evas_Object *frame; //comp object + Evas_Object *frame_object; //frame border comp object Evas_Object *agent; //resize agent; E_Zone *zone; E_Desk *desk; diff --git a/src/bin/e_comp_object.c b/src/bin/e_comp_object.c index 78903c7cf..76e2d7032 100644 --- a/src/bin/e_comp_object.c +++ b/src/bin/e_comp_object.c @@ -3553,7 +3553,7 @@ e_comp_object_frame_theme_set(Evas_Object *obj, const char *name) if (ok) { - cw->frame_object = o; + cw->frame_object = cw->ec->frame_object = o; edje_object_signal_emit(o, "e,version,22", "e"); eina_stringshare_del(cw->frame_theme); cw->frame_theme = theme; diff --git a/src/modules/meson.build b/src/modules/meson.build index 735cd0e77..358824631 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -46,6 +46,7 @@ mods = [ 'packagekit', 'vkbd', 'gesture_recognition', + 'procstats', # modules have a custom binary as well 'battery', 'cpufreq', diff --git a/src/modules/procstats/e-module-procstats.edj b/src/modules/procstats/e-module-procstats.edj new file mode 100644 index 0000000000000000000000000000000000000000..75aa3dc0b66368d2cd5a9ae7af26f77a3a033af3 GIT binary patch literal 12522 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 g(tV=yM7N1X-Nu(8%Pl<*E{$55-ldY$cwL +#include "process.h" + +static Eina_List *_clients = NULL; +static Ecore_Timer *_clients_timer = NULL; + +#define _TIMER_FREQ 3.0 + +typedef struct _Proc_Stats Proc_Stats; +struct _Proc_Stats +{ + E_Client *client; + Evas_Object *obj; + pid_t pid; + uint64_t mem_size; + uint64_t cpu_time; + uint64_t cpu_time_prev; +}; + +static Eina_Bool +_proc_stats_item_exists(E_Client *ec) +{ + Proc_Stats *item; + Eina_List *l; + + EINA_LIST_FOREACH(_clients, l, item) + { + if (item->pid == ec->netwm.pid) return 1; + } + return 0; +} + +static void +_proc_stats_client_del_cb(void *data EINA_UNUSED, Evas *evas EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + edje_object_signal_emit(obj, "e,state,procstats,off", "e"); +} + +static void +_proc_stats_item_add(E_Client *ec) +{ + Evas_Object *o; + Proc_Stats *item; + + if (ec->internal || ec->netwm.pid == -1) return; + if (!ec->frame_object) return; + if (_proc_stats_item_exists(ec)) return; + + o = edje_object_add(evas_object_evas_get(ec->frame)); + + e_theme_edje_object_set(o, "base/theme/borders", + "e/widgets/border/default/border"); + + if (!edje_object_part_exists(o, "e.procstats.text")) return; + + item = calloc(1, sizeof(Proc_Stats)); + EINA_SAFETY_ON_NULL_RETURN(item); + item->pid = ec->netwm.pid; + item->client = ec; + item->obj = ec->frame_object; + + edje_object_signal_emit(item->obj, "e,state,procstats,on", "e"); + evas_object_event_callback_add(item->obj, EVAS_CALLBACK_DEL, _proc_stats_client_del_cb, NULL); + + _clients = eina_list_append(_clients, item); +} + +static void +_proc_stats_item_del(Proc_Stats *item) +{ + edje_object_signal_emit(item->obj, "e,state,procstats,off", "e"); + free(item); +} + +static void +_proc_stats_item_remove(Proc_Stats *item) +{ + Eina_List *l, *l_next; + Proc_Stats *it; + + EINA_LIST_FOREACH_SAFE(_clients, l, l_next, it) + { + if (it == item) + { + _proc_stats_item_del(item); + _clients = eina_list_remove_list(_clients, l); + return; + } + } +} + +static Eina_Bool +_proc_stats_item_gone(Proc_Stats *item) +{ + Eina_List *l; + E_Client *ec; + + EINA_LIST_FOREACH(e_comp->clients, l, ec) + { + if (item->client == ec) return 0; + } + + return 1; +} + +static void +_proc_stats_item_children_update(Eina_List *children, Proc_Stats *item) +{ + Eina_List *l; + Proc_Info *child; + + EINA_LIST_FOREACH(children, l, child) + { + item->mem_size += child->mem_size; + item->cpu_time += child->cpu_time; + if (child->children) + _proc_stats_item_children_update(child->children, item); + } +} + +static void +_proc_stats_item_display(Proc_Stats *item) +{ + Edje_Message_Int_Set *msg; + int mem_size; + + if (item->cpu_time_prev > item->cpu_time) + return; + + if (!item->cpu_time_prev) item->cpu_time_prev = item->cpu_time; + + mem_size = item->mem_size >> 10; + msg = malloc(sizeof(Edje_Message_Int_Set) + (sizeof(int) * 3)); + EINA_SAFETY_ON_NULL_RETURN(msg); + msg->count = 4; + msg->val[0] = eina_cpu_count(); + msg->val[1] = (item->cpu_time - item->cpu_time_prev) / _TIMER_FREQ; + msg->val[2] = mem_size; + msg->val[3] = 0; + edje_object_message_send(item->obj, EDJE_MESSAGE_INT_SET, 1, msg); + free(msg); +} + +static void +_proc_stats_item_update(Eina_List *procs, Proc_Stats *item) +{ + Proc_Info *proc; + Eina_List *l; + + EINA_LIST_FOREACH(procs, l, proc) + { + if (proc->pid == item->pid) + { + item->cpu_time = item->mem_size = 0; + item->mem_size += proc->mem_size; + item->cpu_time += proc->cpu_time; + _proc_stats_item_children_update(proc->children, item); + break; + } + } + _proc_stats_item_display(item); + item->cpu_time_prev = item->cpu_time; +} + +static Eina_Bool +_proc_stats_timer_cb(void *data EINA_UNUSED) +{ + Eina_List *procs, *l; + E_Client *ec; + Proc_Info *proc; + Proc_Stats *item; + + EINA_LIST_FOREACH(e_comp->clients, l, ec) + { + if (!_proc_stats_item_exists(ec)) + _proc_stats_item_add(ec); + } + + procs = proc_info_all_children_get(); + + EINA_LIST_FOREACH(_clients, l, item) + { + if (_proc_stats_item_gone(item)) + _proc_stats_item_remove(item); + else + _proc_stats_item_update(procs, item); + } + + EINA_LIST_FREE(procs, proc) + proc_info_free(proc); + + return 1; +} + +E_API E_Module_Api e_modapi = +{ + E_MODULE_API_VERSION, + "Procstats" +}; + +E_API int +e_modapi_init(E_Module *m EINA_UNUSED) +{ + _proc_stats_timer_cb(NULL); + + _clients_timer = ecore_timer_add(_TIMER_FREQ, _proc_stats_timer_cb, NULL); + + return 1; +} + +E_API int +e_modapi_shutdown(E_Module *m EINA_UNUSED) +{ + Proc_Stats *item; + + if (_clients_timer) + ecore_timer_del(_clients_timer); + + _clients_timer = NULL; + + EINA_LIST_FREE(_clients, item) + _proc_stats_item_del(item); + + _clients = NULL; + + return 1; +} + +E_API int +e_modapi_save(E_Module *m EINA_UNUSED) +{ + return 1; +} diff --git a/src/modules/procstats/meson.build b/src/modules/procstats/meson.build new file mode 100644 index 000000000..ddd429527 --- /dev/null +++ b/src/modules/procstats/meson.build @@ -0,0 +1,12 @@ +deps_custom = declare_dependency(link_args: [] ) +if host_os == 'openbsd' or host_os == 'freebsd' or host_os == 'dragonfly' + deps_custom = declare_dependency(link_args : [ '-lkvm' ]) +endif + +module_deps += deps_custom + +src = files( + 'e_mod_main.c', + 'process.c', + 'process.h', + ) diff --git a/src/modules/procstats/module.desktop b/src/modules/procstats/module.desktop new file mode 100644 index 000000000..844b80324 --- /dev/null +++ b/src/modules/procstats/module.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Link +Name=Procstats +Comment=Monitor window process resources. +Icon=e-module-procstats +X-Enlightenment-ModuleType=system diff --git a/src/modules/procstats/process.c b/src/modules/procstats/process.c new file mode 100644 index 000000000..c47415e3e --- /dev/null +++ b/src/modules/procstats/process.c @@ -0,0 +1,970 @@ +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) +# include +# include +# include +# include +# include +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include "process.h" +#include +#include +#include + +#if defined(__linux__) && !defined(PF_KTHREAD) +# define PF_KTHREAD 0x00200000 +#endif + +#define U64(n) (uint64_t) n +#define MEMSIZE U64 + +static Eina_Bool _show_kthreads = EINA_FALSE; + +#define DO_WE_REALLY_CARE 0 + +void +proc_info_kthreads_show_set(Eina_Bool enabled) +{ + _show_kthreads = enabled; +} + +Eina_Bool +proc_info_kthreads_show_get(void) +{ + return _show_kthreads; +} + +static const char * +_process_state_name(char state) +{ + const char *statename = NULL; +#if defined(__linux__) + + switch (state) + { + case 'D': + statename = "dsleep"; + break; + + case 'I': + statename = "idle"; + break; + + case 'R': + statename = "run"; + break; + + case 'S': + statename = "sleep"; + break; + + case 'T': + case 't': + statename = "stop"; + break; + + case 'X': + statename = "dead"; + break; + + case 'Z': + statename = "zomb"; + break; + } +#else + switch (state) + { + case SIDL: + statename = "idle"; + break; + + case SRUN: + statename = "run"; + break; + + case SSLEEP: + statename = "sleep"; + break; + + case SSTOP: + statename = "stop"; + break; + +#if !defined(__OpenBSD__) + case SWAIT: + statename = "wait"; + break; + + case SLOCK: + statename = "lock"; + break; + +#endif + case SZOMB: + statename = "zomb"; + break; + +#if defined(__OpenBSD__) + case SDEAD: + statename = "dead"; + break; + + case SONPROC: + statename = "onproc"; + break; +#endif + } +#endif + return statename; +} + +#if defined(__linux__) + +static unsigned long +_parse_line(const char *line) +{ + char *p, *tok; + + p = strchr(line, ':') + 1; + while (isspace(*p)) + p++; + tok = strtok(p, " "); + + return atol(tok); +} + +static void +_mem_size(Proc_Info *proc) +{ + FILE *f; + char buf[1024]; + unsigned int dummy, size, shared, resident, data, text; + static int pagesize = 0; + + if (!pagesize) pagesize = getpagesize(); + + f = fopen(eina_slstr_printf("/proc/%d/statm", proc->pid), "r"); + if (!f) return; + + if (fgets(buf, sizeof(buf), f)) + { + if (sscanf(buf, "%u %u %u %u %u %u %u", + &size, &resident, &shared, &text, + &dummy, &data, &dummy) == 7) + { + proc->mem_rss = MEMSIZE(resident) * MEMSIZE(pagesize); + proc->mem_shared = MEMSIZE(shared) * MEMSIZE(pagesize); + proc->mem_size = proc->mem_rss - proc->mem_shared; + proc->mem_virt = MEMSIZE(size) * MEMSIZE(pagesize); + } + } + + fclose(f); +} + +static void +_cmd_args(Proc_Info *p, char *name, size_t len) +{ + char line[4096]; + int pid = p->pid; + + char *link = ecore_file_readlink(eina_slstr_printf("/proc/%d/exe", pid)); + if (link) + { + snprintf(name, len, "%s", ecore_file_file_get(link)); + free(link); + } + + FILE *f = fopen(eina_slstr_printf("/proc/%d/cmdline", pid), "r"); + if (f) + { + if (fgets(line, sizeof(line), f)) + { + Eina_Strbuf *buf = eina_strbuf_new(); + const char *n; + + if (ecore_file_exists(line)) + snprintf(name, len, "%s", ecore_file_file_get(line)); + + n = line; + while (*n && (*n + 1)) + { + eina_strbuf_append(buf, n); + n = strchr(n, '\0') + 1; + if (*n && (*n + 1)) eina_strbuf_append(buf, " "); + } + p->arguments = eina_strbuf_release(buf); + } + fclose(f); + } + + char *end = strchr(name, ' '); + if (end) *end = '\0'; + + p->command = strdup(name); +} + +static int +_uid(int pid) +{ + FILE *f; + int uid = -1; + char line[1024]; + + f = fopen(eina_slstr_printf("/proc/%d/status", pid), "r"); + if (!f) return -1; + + while ((fgets(line, sizeof(line), f)) != NULL) + { + if (!strncmp(line, "Uid:", 4)) + { + uid = _parse_line(line); + break; + } + } + + fclose(f); + + return uid; +} + +static int64_t +_boot_time(void) +{ + FILE *f; + int64_t boot_time; + char buf[4096]; + double uptime = 0.0; + + f = fopen("/proc/uptime", "r"); + if (!f) return 0; + + if (fgets(buf, sizeof(buf), f)) + sscanf(buf, "%lf", &uptime); + else boot_time = 0; + + fclose(f); + + if (uptime > 0.0) + boot_time = time(NULL) - (time_t) uptime; + + return boot_time; +} + +typedef struct { + int pid, ppid, utime, stime, cutime, cstime; + int psr, pri, nice, numthreads; + long long int start_time; + char state; + unsigned int mem_rss, flags; + unsigned long mem_virt; + char name[1024]; +} Stat; + +static Eina_Bool +_stat(const char *path, Stat *st) +{ + FILE *f; + char line[4096]; + int dummy, res = 0; + static int64_t boot_time = 0; + + if (!boot_time) boot_time = _boot_time(); + + memset(st, 0, sizeof(Stat)); + + f = fopen(path, "r"); + if (!f) return EINA_FALSE; + + if (fgets(line, sizeof(line), f)) + { + char *end, *start = strchr(line, '(') + 1; + end = strchr(line, ')'); + + strncpy(st->name, start, end - start); + st->name[end - start] = '\0'; + res = sscanf(end + 2, "%c %d %d %d %d %d %u %u %u %u %u %d %d %d" + " %d %d %d %u %u %lld %lu %u %u %u %u %u %u %u %d %d %d %d %u" + " %d %d %d %d %d %d %d %d %d", + &st->state, &st->ppid, &dummy, &dummy, &dummy, &dummy, &st->flags, + &dummy, &dummy, &dummy, &dummy, &st->utime, &st->stime, &st->cutime, + &st->cstime, &st->pri, &st->nice, &st->numthreads, &dummy, &st->start_time, + &st->mem_virt, &st->mem_rss, &dummy, &dummy, &dummy, &dummy, &dummy, + &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, + &dummy, &dummy, &st->psr, &dummy, &dummy, &dummy, &dummy, &dummy); + } + fclose(f); + + if (res != 42) return EINA_FALSE; + + st->start_time /= sysconf(_SC_CLK_TCK); + st->start_time += boot_time; + + return EINA_TRUE; +} + +static Eina_List * +_process_list_linux_get(void) +{ + Eina_List *files, *list; + char *n; + Stat st; + + list = NULL; + + files = ecore_file_ls("/proc"); + EINA_LIST_FREE(files, n) + { + int pid = atoi(n); + free(n); + + if (!pid) continue; + + if (!_stat(eina_slstr_printf("/proc/%d/stat", pid), &st)) + continue; + + if (st.flags & PF_KTHREAD && !proc_info_kthreads_show_get()) + continue; + + Proc_Info *p = calloc(1, sizeof(Proc_Info)); + if (!p) return NULL; + + p->pid = pid; + p->ppid = st.ppid; + p->uid = _uid(pid); + p->cpu_id = st.psr; + p->start = st.start_time; + p->state = _process_state_name(st.state); + p->cpu_time = st.utime + st.stime; + p->nice = st.nice; + p->priority = st.pri; + p->numthreads = st.numthreads; + _mem_size(p); + _cmd_args(p, st.name, sizeof(st.name)); + + list = eina_list_append(list, p); + } + + return list; +} + +static void +_proc_thread_info(Proc_Info *p) +{ + Eina_List *files; + char *n; + Stat st; + + files = ecore_file_ls(eina_slstr_printf("/proc/%d/task", p->pid)); + EINA_LIST_FREE(files, n) + { + int tid = atoi(n); + free(n); + if (!_stat(eina_slstr_printf("/proc/%d/task/%d/stat", p->pid, tid), &st)) + continue; + + Proc_Info *t = calloc(1, sizeof(Proc_Info)); + if (!t) continue; + t->cpu_id = st.psr; + t->state = _process_state_name(st.state); + t->cpu_time = st.utime + st.stime; + t->nice = st.nice; + t->priority = st.pri; + t->numthreads = st.numthreads; + t->mem_virt = st.mem_virt; + t->mem_rss = st.mem_rss; + + t->tid = tid; + t->thread_name = strdup(st.name); + + p->threads = eina_list_append(p->threads, t); + } +} + +Proc_Info * +proc_info_by_pid(int pid) +{ + Stat st; + + if (!_stat(eina_slstr_printf("/proc/%d/stat", pid), &st)) + return NULL; + + Proc_Info *p = calloc(1, sizeof(Proc_Info)); + if (!p) return NULL; + + p->pid = pid; + p->ppid = st.ppid; + p->uid = _uid(pid); + p->cpu_id = st.psr; + p->start = st.start_time; + p->state = _process_state_name(st.state); + p->cpu_time = st.utime + st.stime; + p->priority = st.pri; + p->nice = st.nice; + p->numthreads = st.numthreads; + _mem_size(p); + _cmd_args(p, st.name, sizeof(st.name)); + + _proc_thread_info(p); + + return p; +} + +#endif + +#if defined(__OpenBSD__) + +static void +_proc_get(Proc_Info *p, struct kinfo_proc *kp) +{ + static int pagesize = 0; + + if (!pagesize) pagesize = getpagesize(); + + p->pid = kp->p_pid; + p->ppid = kp->p_ppid; + p->uid = kp->p_uid; + p->cpu_id = kp->p_cpuid; + p->start = kp->p_ustart_sec; + p->state = _process_state_name(kp->p_stat); + p->cpu_time = kp->p_uticks + kp->p_sticks + kp->p_iticks; + p->mem_virt = p->mem_size = (MEMSIZE(kp->p_vm_tsize) * MEMSIZE(pagesize)) + + (MEMSIZE(kp->p_vm_dsize) * MEMSIZE(pagesize)) + (MEMSIZE(kp->p_vm_ssize) * MEMSIZE(pagesize)); + p->mem_rss = MEMSIZE(kp->p_vm_rssize) * MEMSIZE(pagesize); + p->mem_size = p->mem_rss; + p->priority = kp->p_priority - PZERO; + p->nice = kp->p_nice - NZERO; + p->tid = kp->p_tid; +} + +static void +_cmd_get(Proc_Info *p, kvm_t *kern, struct kinfo_proc *kp) +{ + char **args; + char name[1024]; + + if ((args = kvm_getargv(kern, kp, sizeof(name)-1))) + { + Eina_Strbuf *buf = eina_strbuf_new(); + for (int i = 0; args[i]; i++) + { + eina_strbuf_append(buf, args[i]); + if (args[i + 1]) + eina_strbuf_append(buf, " "); + } + p->arguments = eina_strbuf_string_steal(buf); + eina_strbuf_free(buf); + + if (args[0] && ecore_file_exists(args[0])) + p->command = strdup(ecore_file_file_get(args[0])); + } + + if (!p->command) + p->command = strdup(kp->p_comm); +} + +Proc_Info * +proc_info_by_pid(int pid) +{ + struct kinfo_proc *kp, *kpt; + kvm_t *kern; + char errbuf[_POSIX2_LINE_MAX]; + int count, pid_count; + + kern = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); + if (!kern) return NULL; + + kp = kvm_getprocs(kern, KERN_PROC_PID, pid, sizeof(*kp), &count); + if (!kp) return NULL; + + if (count == 0) return NULL; + + Proc_Info *p = calloc(1, sizeof(Proc_Info)); + if (!p) return NULL; + + _proc_get(p, kp); + _cmd_get(p, kern, kp); + + kp = kvm_getprocs(kern, KERN_PROC_SHOW_THREADS, 0, sizeof(*kp), &pid_count); + + for (int i = 0; i < pid_count; i++) + { + if (kp[i].p_pid != p->pid) continue; + + kpt = &kp[i]; + + if (kpt->p_tid <= 0) continue; + + Proc_Info *t = calloc(1, sizeof(Proc_Info)); + if (!t) continue; + + _proc_get(t, kpt); + + t->tid = kpt->p_tid; + t->thread_name = strdup(kpt->p_comm); + + p->threads = eina_list_append(p->threads, t); + } + + p->numthreads = eina_list_count(p->threads); + + kvm_close(kern); + + return p; +} + +static Eina_List * +_process_list_openbsd_get(void) +{ + struct kinfo_proc *kps, *kp; + Proc_Info *p; + char errbuf[4096]; + kvm_t *kern; + int pid_count; + Eina_List *list = NULL; + + kern = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); + if (!kern) return NULL; + + kps = kvm_getprocs(kern, KERN_PROC_ALL, 0, sizeof(*kps), &pid_count); + if (!kps) return NULL; + + for (int i = 0; i < pid_count; i++) + { + p = calloc(1, sizeof(Proc_Info)); + if (!p) return NULL; + + kp = &kps[i]; + + _proc_get(p, kp); + _cmd_get(p, kern, kp); + + list = eina_list_append(list, p); + } + + kvm_close(kern); + + return list; +} + +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) + +static int +_pid_max(void) +{ + size_t len; + static int pid_max = 0; + + if (pid_max != 0) return pid_max; + + len = sizeof(pid_max); + if (sysctlbyname("kern.pid_max", &pid_max, &len, NULL, 0) == -1) + { +#if defined(__FreeBSD__) + pid_max = 99999; +#elif defined(__DragonFly__) + pid_max = 999999; +#else + pid_max = PID_MAX; +#endif + } + + return pid_max; +} + +static void +_cmd_get(Proc_Info *p, struct kinfo_proc *kp) +{ + kvm_t * kern; + char **args; + char name[1024]; + Eina_Bool have_command = EINA_FALSE; + + kern = kvm_open(NULL, "/dev/null", NULL, O_RDONLY, "kvm_open"); + if (kern) + { + if ((args = kvm_getargv(kern, kp, sizeof(name)-1)) && (args[0])) + { + char *base = strdup(args[0]); + if (base) + { + char *spc = strchr(base, ' '); + if (spc) *spc = '\0'; + + if (ecore_file_exists(base)) + { + snprintf(name, sizeof(name), "%s", basename(base)); + have_command = EINA_TRUE; + } + free(base); + } + Eina_Strbuf *buf = eina_strbuf_new(); + for (int i = 0; args[i] != NULL; i++) + { + eina_strbuf_append(buf, args[i]); + if (args[i + 1]) + eina_strbuf_append(buf, " "); + } + p->arguments = eina_strbuf_string_steal(buf); + eina_strbuf_free(buf); + } + kvm_close(kern); + } + + if (!have_command) + snprintf(name, sizeof(name), "%s", kp->ki_comm); + + p->command = strdup(name); +} + +static Proc_Info * +_proc_thread_info(struct kinfo_proc *kp, Eina_Bool is_thread) +{ + struct rusage *usage; + Proc_Info *p; + static int pagesize = 0; + + if (!pagesize) pagesize = getpagesize(); + + p = calloc(1, sizeof(Proc_Info)); + if (!p) return NULL; + + p->pid = kp->ki_pid; + p->ppid = kp->ki_ppid; + p->uid = kp->ki_uid; + + if (!is_thread) + _cmd_get(p, kp); + + p->cpu_id = kp->ki_oncpu; + if (p->cpu_id == -1) + p->cpu_id = kp->ki_lastcpu; + + usage = &kp->ki_rusage; + + p->cpu_time = (usage->ru_utime.tv_sec * 1000000) + usage->ru_utime.tv_usec + + (usage->ru_stime.tv_sec * 1000000) + usage->ru_stime.tv_usec; + // XXX: See kern.sched.idlespins + p->cpu_time /= 10000; + p->state = _process_state_name(kp->ki_stat); + p->mem_virt = kp->ki_size; + p->mem_rss = MEMSIZE(kp->ki_rssize) * MEMSIZE(pagesize); + p->start = kp->ki_start.tv_sec; + p->mem_size = p->mem_rss; + p->nice = kp->ki_nice - NZERO; + p->priority = kp->ki_pri.pri_level - PZERO; + p->numthreads = kp->ki_numthreads; + + p->tid = kp->ki_tid; + p->thread_name = strdup(kp->ki_tdname); + + return p; +} + +static Eina_List * +_process_list_freebsd_fallback_get(void) +{ + Eina_List *list; + struct kinfo_proc kp; + int mib[4]; + size_t len; + static int pid_max; + + pid_max = _pid_max(); + + list = NULL; + + len = sizeof(int); + if (sysctlnametomib("kern.proc.pid", mib, &len) == -1) + return NULL; + + for (int i = 1; i <= pid_max; i++) + { + mib[3] = i; + len = sizeof(kp); + if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) + continue; + + if (kp.ki_flag & P_KPROC && !proc_info_kthreads_show_get()) + continue; + + Proc_Info *p = _proc_thread_info(&kp, EINA_FALSE); + if (p) + list = eina_list_append(list, p); + } + + return list; +} + +static Eina_List * +_process_list_freebsd_get(void) +{ + kvm_t *kern; + Eina_List *list = NULL; + struct kinfo_proc *kps, *kp; + char errbuf[_POSIX2_LINE_MAX]; + int pid_count; + + kern = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); + if (!kern) + return _process_list_freebsd_fallback_get(); + + kps = kvm_getprocs(kern, KERN_PROC_PROC, 0, &pid_count); + if (!kps) + { + kvm_close(kern); + return _process_list_freebsd_fallback_get(); + } + + for (int i = 0; i < pid_count; i++) + { + if (kps[i].ki_flag & P_KPROC && !proc_info_kthreads_show_get()) + continue; + + kp = &kps[i]; + + Proc_Info *p = _proc_thread_info(kp, EINA_FALSE); + if (p) + list = eina_list_append(list, p); + } + + kvm_close(kern); + + return list; +} + +static Proc_Info * +_proc_info_by_pid_fallback(int pid) +{ + struct kinfo_proc kp; + int mib[4]; + size_t len; + + len = sizeof(int); + if (sysctlnametomib("kern.proc.pid", mib, &len) == -1) + return NULL; + + mib[3] = pid; + + len = sizeof(kp); + if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) + return NULL; + + Proc_Info *p = _proc_thread_info(&kp, EINA_FALSE); + + return p; +} + +Proc_Info * +proc_info_by_pid(int pid) +{ + kvm_t *kern; + struct kinfo_proc *kps, *kp; + char errbuf[_POSIX2_LINE_MAX]; + int pid_count; + + kern = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); + if (!kern) + return _proc_info_by_pid_fallback(pid); + + kps = kvm_getprocs(kern, KERN_PROC_ALL, 0, &pid_count); + if (!kps) + { + kvm_close(kern); + return _proc_info_by_pid_fallback(pid); + } + + Proc_Info *p = NULL; + + for (int i = 0; i < pid_count; i++) + { + if (kps[i].ki_flag & P_KPROC && !proc_info_kthreads_show_get()) + continue; + if (kps[i].ki_pid != pid) + continue; + + kp = &kps[i]; + Proc_Info *t = _proc_thread_info(kp, EINA_TRUE); + if (!p) + { + p = _proc_thread_info(kp, EINA_FALSE); + p->cpu_time = 0; + } + + p->cpu_time += t->cpu_time; + p->threads = eina_list_append(p->threads, t); + } + + kvm_close(kern); + + if (!p) return _proc_info_by_pid_fallback(pid); + + return p; +} +#endif + +void +proc_info_free(Proc_Info *proc) +{ + Proc_Info *t; + + EINA_LIST_FREE(proc->threads, t) + { + proc_info_free(t); + } + + if (proc->children) + eina_list_free(proc->children); + + if (proc->command) + free(proc->command); + if (proc->arguments) + free(proc->arguments); + if (proc->thread_name) + free(proc->thread_name); + + free(proc); +} + +Eina_List * +proc_info_all_get(void) +{ + Eina_List *processes; + +#if defined(__linux__) + processes = _process_list_linux_get(); +#elif defined(__FreeBSD__) || defined(__DragonFly__) + processes = _process_list_freebsd_get(); +#elif defined(__OpenBSD__) + processes = _process_list_openbsd_get(); +#else + processes = NULL; +#endif + + return processes; +} + +static Eina_Bool +_child_add(Eina_List *parents, Proc_Info *child) +{ + Eina_List *l; + Proc_Info *parent; + + EINA_LIST_FOREACH(parents, l, parent) + { + if (parent->pid == child->ppid) + { + parent->children = eina_list_append(parent->children, child); + return 1; + } + } + + return 0; +} + +Eina_List * +proc_info_all_children_get() +{ + Proc_Info *proc; + Eina_List *l; + Eina_List *procs; + + procs = proc_info_all_get(); + + EINA_LIST_FOREACH(procs, l, proc) + { + int ok =_child_add(procs, proc); + (void) ok; + } + + return procs; +} + +Eina_List * +_append_wanted(Eina_List *wanted, Eina_List *tree) +{ + Eina_List *l; + Proc_Info *parent; + + EINA_LIST_FOREACH(tree, l, parent) + { + wanted = eina_list_append(wanted, parent); + if (parent->children) + wanted = _append_wanted(wanted, parent->children); + } + return wanted; +} + +Eina_List * +proc_info_pid_children_get(pid_t pid) +{ + Proc_Info *proc; + Eina_List *l, *procs, *wanted = NULL; + + procs = proc_info_all_children_get(); + + EINA_LIST_FOREACH(procs, l, proc) + { + if (!wanted && proc->pid == pid) + { + wanted = eina_list_append(wanted, proc); + if (proc->children) + wanted = _append_wanted(wanted, proc->children); + } + } + + EINA_LIST_FREE(procs, proc) + { + if (!eina_list_data_find(wanted, proc)) + { + proc_info_free(proc); + } + } + + return wanted; +} + +void +proc_info_all_children_free(Eina_List *pstree) +{ + Proc_Info *parent, *child; + + EINA_LIST_FREE(pstree, parent) + { + EINA_LIST_FREE(parent->children, child) + proc_info_pid_children_free(child); + proc_info_free(parent); + } +} + +void +proc_info_pid_children_free(Proc_Info *proc) +{ + Proc_Info *child; + + EINA_LIST_FREE(proc->children, child) + proc_info_free(child); + + proc_info_free(proc); +} + diff --git a/src/modules/procstats/process.h b/src/modules/procstats/process.h new file mode 100644 index 000000000..83da6aec6 --- /dev/null +++ b/src/modules/procstats/process.h @@ -0,0 +1,65 @@ +#ifndef __PROC_H__ +#define __PROC_H__ + +#include +#include +#include + +#if !defined(PID_MAX) +# define PID_MAX 99999 +#endif + +typedef struct _Proc_Info +{ + pid_t pid; + pid_t ppid; + uid_t uid; + int8_t nice; + int8_t priority; + int cpu_id; + int32_t numthreads; + int64_t cpu_time; + double cpu_usage; + int64_t start; + + uint64_t mem_size; + uint64_t mem_virt; + uint64_t mem_rss; + uint64_t mem_shared; + + char *command; + char *arguments; + const char *state; + + int tid; + char *thread_name; + + Eina_List *threads; + Eina_List *children; +} Proc_Info; + +Eina_List * +proc_info_all_get(void); + +Proc_Info * +proc_info_by_pid(int pid); + +void +proc_info_free(Proc_Info *proc); + +void +proc_info_kthreads_show_set(Eina_Bool enabled); + +Eina_Bool +proc_info_kthreads_show_get(void); + +Eina_List * +proc_info_all_children_get(void); + +Eina_List * +proc_info_pid_children_get(pid_t pid); + +void +proc_info_pid_children_free(Proc_Info *procs); + +#endif