edi_process: Introduce new process API.

Introduced process querying API for all the platforms
we currently support. With this we are able to get
realiable data about system processes.

We use this API to track running process instances
launched from Edi.

Also, this allows for a much cleaner implementation
of the edi_debug code when tracking the debug process.

Essentially using the new API to track both running
instances and debugged instances.

Moreover, simplifying this by using a timer in
edi_main to keep track of these occurences.

We can now reliably track use of the 'Run' command
in the toolbar and menu. Adding a 'Terminate' to
compliment. The UI now responds properly to running
or non-running Edi launched process instances.
This commit is contained in:
Alastair Poole 2018-11-25 17:05:16 +00:00
parent 1ce49c9e8a
commit 06d866411d
15 changed files with 631 additions and 165 deletions

View File

@ -2,21 +2,19 @@
# include "config.h"
#endif
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined (__APPLE__) || defined(__OpenBSD__)
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
#include <unistd.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#endif
#if defined(__OpenBSD__)
#include <sys/proc.h>
#endif
#include <Eo.h>
#include <Eina.h>
#include <Elementary.h>
#include "edi_debug.h"
#include "edi_process.h"
#include "edi_config.h"
#include "edi_private.h"
@ -67,160 +65,77 @@ Edi_Debug_Tool *edi_debug_tool_get(const char *name)
return &_debugger_tools[0];
}
#if defined(__FreeBSD__) || defined(__DragonFly__)
static long int
_sysctlfromname(const char *name, void *mib, int depth, size_t *len)
static int
_system_pid_max_get(void)
{
long int result;
static int pid_max = 0;
if (sysctlnametomib(name, mib, len) < 0) return -1;
*len = sizeof(result);
if (sysctl(mib, depth, &result, len, NULL, 0) < 0) return -1;
if (pid_max > 0)
return pid_max;
return result;
}
#if defined(__linux__)
FILE *f;
char buf[128];
size_t n;
f = fopen("/proc/sys/kernel/pid_max", "r");
if (f)
{
n = fread(buf, 1, sizeof(buf) - 1, f);
buf[n] = 0x00;
fclose(f);
pid_max = atoi(buf);
}
#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
int mib[2] = { CTL_KERN, KERN_MAXPROC };
size_t len = sizeof(pid_max);
sysctl(mib, 2, &pid_max, &len, NULL, 0);
#elif defined(PID_MAX)
pid_max = PID_MAX;
#endif
if (pid_max <= 0)
pid_max = 99999;
return pid_max;
}
/* Get the process ID of the child process being debugged in *our* session */
int edi_debug_process_id(Edi_Debug *debugger)
{
int my_pid, child_pid = -1;
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined (__APPLE__) || defined(__OpenBSD__)
struct kinfo_proc kp;
int mib[6];
size_t len;
int max_pid, i;
Edi_Proc_Stats *p;
int pid_max, debugger_pid, child_pid = -1;
if (!debugger) return -1;
if (!debugger->program_name) return -1;
if (!debugger->exe) return -1;
#if defined(__FreeBSD__) || defined(__DragonFly__)
len = sizeof(max_pid);
max_pid = _sysctlfromname("kern.lastpid", mib, 2, &len);
#elif defined(PID_MAX)
max_pid = PID_MAX;
#else
max_pid = 99999;
#endif
my_pid = ecore_exe_pid_get(debugger->exe);
debugger_pid = ecore_exe_pid_get(debugger->exe);
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__)
if (sysctlnametomib("kern.proc.pid", mib, &len) < 0) return -1;
#elif defined(__OpenBSD__)
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[4] = sizeof(struct kinfo_proc);
mib[5] = 1;
#endif
pid_max = _system_pid_max_get();
for (i = my_pid; i <= max_pid; i++)
for (int i = 1; i <= pid_max; i++)
{
mib[3] = i;
len = sizeof(kp);
#if defined(__OpenBSD__)
if (sysctl(mib, 6, &kp, &len, NULL, 0) == -1) continue;
#else
if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) continue;
#endif
#if defined(__FreeBSD__) || defined(__DragonFly__)
if (kp.ki_ppid != my_pid) continue;
if (strcmp(debugger->program_name, kp.ki_comm)) continue;
child_pid = kp.ki_pid;
if (kp.ki_stat == SRUN || kp.ki_stat == SSLEEP)
debugger->state = EDI_DEBUG_PROCESS_ACTIVE;
else
debugger->state = EDI_DEBUG_PROCESS_SLEEPING;
#elif defined(__OpenBSD__)
if (kp.p_ppid != my_pid) continue;
if (strcmp(debugger->program_name, kp.p_comm)) continue;
child_pid = kp.p_pid;
if (kp.p_stat == SRUN || kp.p_stat == SSLEEP)
debugger->state = EDI_DEBUG_PROCESS_ACTIVE;
else
debugger->state = EDI_DEBUG_PROCESS_SLEEPING;
#else /* APPLE */
if (kp.kp_proc.p_oppid != my_pid) continue;
if (strcmp(debugger->program_name, kp.kp_proc.p_comm)) continue;
child_pid = kp.kp_proc.p_pid;
if (kp.kp_proc.p_stat == SRUN || kp.kp_proc.p_stat == SSLEEP)
debugger->state = EDI_DEBUG_PROCESS_ACTIVE;
else
debugger->state = EDI_DEBUG_PROCESS_SLEEPING;
#endif
break;
}
#else
Eina_List *files, *l;
const char *temp_name;
char path[PATH_MAX];
char buf[4096];
char *p, *name, *end;
FILE *f;
int count, parent_pid, pid;
if (!debugger->program_name)
return -1;
if (!debugger->exe) return -1;
my_pid = ecore_exe_pid_get(debugger->exe);
files = ecore_file_ls("/proc");
EINA_LIST_FOREACH(files, l, name)
{
pid = atoi(name);
if (!pid) continue;
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
f = fopen(path, "r");
if (!f) continue;
p = fgets(buf, sizeof(buf), f);
fclose(f);
p = edi_process_stats_by_pid(i);
if (!p) continue;
temp_name = ecore_file_file_get(buf);
if (!strcmp(temp_name, debugger->program_name))
if (p->ppid == debugger_pid)
{
parent_pid = 0;
// Match success - program name with pid.
child_pid = pid;
snprintf(path, sizeof(path), "/proc/%d/stat", pid);
f = fopen(path, "r");
if (f)
if (!strcmp(debugger->program_name, p->command))
{
count = 0;
p = fgets(buf, sizeof(buf), f);
while (*p++ != '\0')
{
if (p[0] == ' ') { count++; p++; }
if (count == 2)
{
if (p[0] == 'S' || p[0] == 'R')
debugger->state = EDI_DEBUG_PROCESS_ACTIVE;
else
debugger->state = EDI_DEBUG_PROCESS_SLEEPING;
}
if (count == 3) break;
}
end = strchr(p, ' ');
if (end)
{
*end = '\0';
// parent pid matches - right process.
parent_pid = atoi(p);
}
fclose(f);
child_pid = p->pid;
if (!strcmp(p->state, "RUN") ||!strcmp(p->state, "SLEEP"))
debugger->state = EDI_DEBUG_PROCESS_ACTIVE;
else
debugger->state = EDI_DEBUG_PROCESS_SLEEPING;
}
if (parent_pid == my_pid)
break;
}
free(p);
if (child_pid != -1)
break;
}
if (files)
eina_list_free(files);
#endif
return child_pid;
}

View File

@ -197,11 +197,14 @@ _edi_debugpanel_button_start_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UN
edi_debugpanel_start(_edi_project_config_debug_command_get());
}
static Eina_Bool
_edi_debug_active_check_cb(void *data EINA_UNUSED)
void
edi_debugpanel_active_check(void)
{
Edi_Debug *debug;
int pid;
Edi_Debug *debug = edi_debug_get();
debug = edi_debug_get();
if (!debug) return;
pid = ecore_exe_pid_get(debug->exe);
if (pid == -1)
@ -212,16 +215,11 @@ _edi_debug_active_check_cb(void *data EINA_UNUSED)
elm_object_disabled_set(_button_start, EINA_FALSE);
elm_object_disabled_set(_button_int, EINA_TRUE);
elm_object_disabled_set(_button_term, EINA_TRUE);
return ECORE_CALLBACK_RENEW;
return;
}
if (!debug)
return ECORE_CALLBACK_RENEW;
pid = edi_debug_process_id(debug);
_edi_debugpanel_icons_update(pid > 0 ? debug->state : 0);
return ECORE_CALLBACK_RENEW;
}
void edi_debugpanel_stop(void)
@ -322,7 +320,6 @@ void edi_debugpanel_add(Evas_Object *parent)
Evas_Object *ico_start, *ico_quit, *ico_int, *ico_term;
Elm_Code_Widget *widget;
Elm_Code *code;
Ecore_Timer *timer;
frame = elm_frame_add(parent);
elm_object_text_set(frame, _("Debug"));
@ -409,9 +406,6 @@ void edi_debugpanel_add(Evas_Object *parent)
edi_debug_new();
timer = ecore_timer_add(1.0, _edi_debug_active_check_cb, NULL);
(void) timer;
elm_box_pack_end(box, widget);
elm_box_pack_end(box, table);
evas_object_show(box);

View File

@ -47,6 +47,14 @@ void edi_debugpanel_start(const char *toolname);
*/
void edi_debugpanel_stop();
/**
* Perform a poll to check the state of any debug session
*
* @ingroup UI
*/
void edi_debugpanel_active_check(void);
/**
* @}
*/

View File

@ -16,6 +16,7 @@
#include "edi_theme.h"
#include "edi_filepanel.h"
#include "edi_file.h"
#include "edi_process.h"
#include "edi_logpanel.h"
#include "edi_consolepanel.h"
#include "edi_searchpanel.h"
@ -52,12 +53,64 @@ static Elm_Object_Item *_edi_selected_bottompanel;
static Evas_Object *_edi_filepanel, *_edi_filepanel_icon;
static Evas_Object *_edi_menu_undo, *_edi_menu_redo, *_edi_toolbar_undo, *_edi_toolbar_redo, *_edi_toolbar_build, *_edi_toolbar_test;
static Evas_Object *_edi_menu_build, *_edi_menu_clean, *_edi_menu_test;
static Evas_Object *_edi_menu_build, *_edi_menu_clean, *_edi_menu_test, *_edi_menu_run, *_edi_menu_terminate;
static Evas_Object *_edi_menu_init, *_edi_menu_commit, *_edi_menu_push, *_edi_menu_pull, *_edi_menu_status, *_edi_menu_stash;
static Evas_Object *_edi_menu_save, *_edi_toolbar_save;
static Evas_Object *_edi_main_win, *_edi_main_box;
static Evas_Object *_edi_toolbar_run, *_edi_toolbar_terminate;
int _edi_log_dom = -1;
static void
_edi_active_process_icons_set(Eina_Bool active)
{
if (active)
{
elm_object_disabled_set(_edi_toolbar_run, EINA_TRUE);
elm_object_disabled_set(_edi_toolbar_terminate, EINA_FALSE);
elm_object_item_disabled_set(_edi_menu_run, EINA_TRUE);
elm_object_item_disabled_set(_edi_menu_terminate, EINA_FALSE);
}
else
{
elm_object_disabled_set(_edi_toolbar_run, EINA_FALSE);
elm_object_disabled_set(_edi_toolbar_terminate, EINA_TRUE);
elm_object_item_disabled_set(_edi_menu_run, EINA_FALSE);
elm_object_item_disabled_set(_edi_menu_terminate, EINA_TRUE);
}
}
static Eina_Bool
_edi_active_process_check_cb(EINA_UNUSED void *data)
{
Edi_Proc_Stats *stats;
pid_t pid;
// Check debugpanel state.
edi_debugpanel_active_check();
pid = edi_exe_project_pid_get();
if (pid == -1)
{
_edi_active_process_icons_set(EINA_FALSE);
return ECORE_CALLBACK_RENEW;
}
stats = edi_process_stats_by_pid(pid);
if (!stats)
{
// Our process is not running, reset PID we
// track.
edi_exe_project_pid_reset();
_edi_active_process_icons_set(EINA_FALSE);
return ECORE_CALLBACK_RENEW;
}
free(stats);
_edi_active_process_icons_set(EINA_TRUE);
return ECORE_CALLBACK_RENEW;
}
static void
_edi_file_open_cb(const char *path, const char *type, Eina_Bool newwin)
@ -722,6 +775,15 @@ _edi_launcher_run(Edi_Project_Config_Launch *launch)
edi_builder_run(launch->path, launch->args);
}
static void
_edi_launcher_terminate(void)
{
pid_t pid = edi_exe_project_pid_get();
if (pid == -1) return;
kill(pid, SIGKILL);
}
static void
_edi_build_menu_items_disabled_set(Eina_Bool state)
{
@ -831,6 +893,12 @@ _tb_debug_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_i
_edi_debug_project();
}
static void
_tb_terminate_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
_edi_launcher_terminate();
}
static void
_tb_about_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
@ -1037,6 +1105,13 @@ _edi_menu_run_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
_edi_launcher_run(&_edi_project_config->launch);
}
static void
_edi_menu_terminate_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
_edi_launcher_terminate();
}
static void
_edi_menu_clean_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
@ -1227,9 +1302,11 @@ _edi_menu_setup(Evas_Object *win)
menu_it = elm_menu_item_add(menu, NULL, NULL, _("Build"), NULL, NULL);
_edi_menu_build = elm_menu_item_add(menu, menu_it, "system-run", _("Build"), _edi_menu_build_cb, NULL);
_edi_menu_test = elm_menu_item_add(menu, menu_it, "media-record", _("Test"), _edi_menu_test_cb, NULL);
elm_menu_item_add(menu, menu_it, "media-playback-start", _("Run"), _edi_menu_run_cb, NULL);
_edi_menu_clean = elm_menu_item_add(menu, menu_it, "edit-clear", _("Clean"), _edi_menu_clean_cb, NULL);
elm_menu_item_separator_add(menu, menu_it);
_edi_menu_run = elm_menu_item_add(menu, menu_it, "media-playback-start", _("Run"), _edi_menu_run_cb, NULL);
_edi_menu_terminate = elm_menu_item_add(menu, menu_it, "media-playback-stop", _("Terminate"), _edi_menu_terminate_cb, NULL);
elm_menu_item_separator_add(menu, menu_it);
elm_menu_item_add(menu, menu_it, "utilities-terminal", _("Debugger"), _edi_menu_debug_cb, NULL);
elm_menu_item_add(menu, menu_it, "applications-electronics", _("Memcheck"), _edi_menu_memcheck_cb, NULL);
@ -1304,7 +1381,10 @@ edi_toolbar_setup(Evas_Object *parent)
_edi_toolbar_build = _edi_toolbar_item_add(tb, "system-run", _("Build"), _tb_build_cb);
_edi_toolbar_test = _edi_toolbar_item_add(tb, "media-record", _("Test"), _tb_test_cb);
_edi_toolbar_item_add(tb, "media-playback-start", _("Run"), _tb_run_cb);
tb_it = elm_toolbar_item_append(tb, "separator", "", NULL, NULL);
elm_toolbar_item_separator_set(tb_it, EINA_TRUE);
_edi_toolbar_run =_edi_toolbar_item_add(tb, "media-playback-start", _("Run"), _tb_run_cb);
_edi_toolbar_terminate = _edi_toolbar_item_add(tb, "media-playback-stop", _("Terminate"), _tb_terminate_cb);
_edi_toolbar_item_add(tb, "utilities-terminal", _("Debug"), _tb_debug_cb);
tb_it = elm_toolbar_item_append(tb, "separator", "", NULL, NULL);
@ -1581,6 +1661,7 @@ edi_open(const char *inputpath)
ecore_event_handler_add(EDI_EVENT_TAB_CHANGED, _edi_tab_changed, NULL);
ecore_event_handler_add(EDI_EVENT_FILE_CHANGED, _edi_file_changed, NULL);
ecore_event_handler_add(EDI_EVENT_FILE_SAVED, _edi_file_saved, NULL);
ecore_timer_add(1.0, _edi_active_process_check_cb, NULL);
ERR("Loaded project at %s", path);
evas_object_resize(win, _edi_project_config->gui.width * elm_config_scale_get(),

View File

@ -26,7 +26,7 @@ _relative_path_exists(const char *base, const char *relative)
static void
_exec_cmd(const char *cmd)
{
ecore_exe_pipe_run(cmd,
edi_exe_project_run(cmd,
ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ |
ECORE_EXE_PIPE_ERROR_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR |
ECORE_EXE_PIPE_WRITE | ECORE_EXE_USE_SH, NULL);

View File

@ -76,7 +76,7 @@ _cmake_run(const char *path, const char *args)
if (!args)
{
ecore_exe_pipe_run(path, ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ |
edi_exe_project_run(path, ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ |
ECORE_EXE_PIPE_ERROR_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR |
ECORE_EXE_PIPE_WRITE | ECORE_EXE_USE_SH, NULL);
@ -87,7 +87,7 @@ _cmake_run(const char *path, const char *args)
full_cmd = malloc(sizeof(char) * (full_len + 1));
snprintf(full_cmd, full_len + 2, "%s %s", path, args);
ecore_exe_pipe_run(full_cmd, ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ |
edi_exe_project_run(full_cmd, ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ |
ECORE_EXE_PIPE_ERROR_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR |
ECORE_EXE_PIPE_WRITE | ECORE_EXE_USE_SH, NULL);

View File

@ -69,22 +69,40 @@ _go_test(void)
static void
_go_run(const char *path EINA_UNUSED, const char *args EINA_UNUSED)
{
const char *ext;
char *full_cmd;
int full_len;
int full_len, flags;
if (!path) return;
if (chdir(edi_project_get()) !=0)
ERR("Could not chdir");
full_len = strlen(path) + 8;
if (args)
full_len += strlen(args);
full_cmd = malloc(sizeof(char) * (full_len + 1));
snprintf(full_cmd, full_len + 1, "go run %s %s", path, args?args:"");
ecore_exe_pipe_run(full_cmd, ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ |
ECORE_EXE_PIPE_ERROR_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR |
ECORE_EXE_PIPE_WRITE | ECORE_EXE_USE_SH, NULL);
full_cmd = malloc(sizeof(char) * (full_len + 1));
flags = ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ |
ECORE_EXE_PIPE_ERROR_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR |
ECORE_EXE_PIPE_WRITE;
ext = strrchr(path, '.');
// We may want to run a binary or via the go command.
// Simple and quicker to test for file extension.
if (ext && !strcasecmp(ext, ".go"))
{
snprintf(full_cmd, full_len + 1, "go run %s %s", path, args?args:"");
flags |= ECORE_EXE_USE_SH;
}
else
{
snprintf(full_cmd, full_len + 1, "%s", path);
}
edi_exe_project_run(full_cmd, flags, NULL);
free(full_cmd);
}

View File

@ -143,7 +143,7 @@ _make_run(const char *path, const char *args)
if (!args)
{
ecore_exe_pipe_run(path, ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ |
edi_exe_project_run(path, ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ |
ECORE_EXE_PIPE_ERROR_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR |
ECORE_EXE_PIPE_WRITE | ECORE_EXE_USE_SH, NULL);
@ -154,7 +154,7 @@ _make_run(const char *path, const char *args)
full_cmd = malloc(sizeof(char) * (full_len + 1));
snprintf(full_cmd, full_len + 1, "%s %s", path, args);
ecore_exe_pipe_run(full_cmd, ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ |
edi_exe_project_run(full_cmd, ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ |
ECORE_EXE_PIPE_ERROR_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR |
ECORE_EXE_PIPE_WRITE | ECORE_EXE_USE_SH, NULL);

View File

@ -147,7 +147,8 @@ _meson_run(const char *path, const char *args)
if (args) cmd = eina_slstr_printf("%s %s", path, args);
else cmd = path;
ecore_exe_pipe_run(cmd,
edi_exe_project_run(cmd,
ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ |
ECORE_EXE_PIPE_ERROR_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR |
ECORE_EXE_PIPE_WRITE /*| ECORE_EXE_USE_SH*/, md);

View File

@ -26,7 +26,7 @@ _relative_path_exists(const char *base, const char *relative)
static void
_exec_cmd(const char *cmd)
{
ecore_exe_pipe_run(cmd,
edi_exe_project_run(cmd,
ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_READ |
ECORE_EXE_PIPE_ERROR_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR |
ECORE_EXE_PIPE_WRITE | ECORE_EXE_USE_SH, NULL);

View File

@ -184,3 +184,29 @@ edi_exe_response(const char *command)
return out;
}
static pid_t _project_pid = -1;
void
edi_exe_project_pid_reset()
{
_project_pid = -1;
}
pid_t
edi_exe_project_pid_get(void)
{
return _project_pid;
}
pid_t
edi_exe_project_run(const char *command, int flags, void *data)
{
Ecore_Exe *exe;
exe = ecore_exe_pipe_run(command, flags, data);
_project_pid = ecore_exe_pid_get(exe);
return _project_pid;
}

View File

@ -62,6 +62,31 @@ EAPI void edi_exe_notify(const char *name, const char *command);
*/
EAPI Eina_Bool edi_exe_notify_handle(const char *name, void ((*func)(int, void *)), void *data);
/**
* This function launches the project application binary. Used for monitoring a running
* process state. It's a wrapper for ecore_exe_run but is necessary to keep track of any
* instance of program being ran.
*
* @param command The command to execute.
* @param flags The ECORE_EXE flags to execute with.
* @param data Data to be passed to ecore_exe_run.
*
* @return PID of the process after executing.
*/
EAPI pid_t edi_exe_project_run(const char *command, int flags, void *data);
/**
* Returns the PID of the project executable if running.
*
* @return PID if the process exists else -1.
*/
EAPI pid_t edi_exe_project_pid_get(void);
/**
* Reset the project PID to inactive state.
*/
EAPI void edi_exe_project_pid_reset(void);
/**
* @}
*/

335
src/lib/edi_process.c Normal file
View File

@ -0,0 +1,335 @@
#if defined(__MACH__) && defined(__APPLE__)
# define __MacOS__
#endif
#if defined(__MacOS__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
# include <sys/types.h>
# include <sys/sysctl.h>
# include <sys/user.h>
# include <sys/proc.h>
#endif
#if defined(__OpenBSD__)
# include <kvm.h>
# include <limits.h>
# include <sys/proc.h>
# include <sys/param.h>
# include <sys/resource.h>
#endif
#if defined(__MacOS__)
# include <libproc.h>
# include <sys/proc_info.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <limits.h>
#include "edi_process.h"
#include <Eina.h>
#include <Ecore.h>
#include <Ecore_File.h>
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(__MacOS__)
#if !defined(__OpenBSD__)
case SWAIT:
statename = "WAIT";
break;
case SLOCK:
statename = "LOCK";
break;
#endif
case SZOMB:
statename = "ZOMB";
break;
#endif
#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);
}
Edi_Proc_Stats *
edi_process_stats_by_pid(int pid)
{
FILE *f;
char path[PATH_MAX];
char line[4096];
char state, program_name[1024];
int res, dummy, utime, stime, cutime, cstime, uid, psr, ppid;
unsigned int mem_size, mem_rss, pri, nice, numthreads;
snprintf(path, sizeof(path), "/proc/%d/stat", pid);
if (!ecore_file_exists(path))
return NULL;
f = fopen(path, "r");
if (!f) return NULL;
if (fgets(line, sizeof(line), f))
{
char *end, *start = strchr(line, '(') + 1;
end = strchr(line, ')');
strncpy(program_name, start, end - start);
program_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 %d %u %u %u %u %u %u %u %u %d %d %d %d %u %d %d %d %d %d %d %d %d %d",
&state, &ppid, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &utime, &stime, &cutime, &cstime,
&pri, &nice, &numthreads, &dummy, &dummy, &mem_size, &mem_rss, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy,
&dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &psr, &dummy, &dummy, &dummy, &dummy, &dummy);
}
fclose(f);
if (res != 42) return NULL;
snprintf(path, sizeof(path), "/proc/%d/status", pid);
f = fopen(path, "r");
if (!f) return NULL;
while ((fgets(line, sizeof(line), f)) != NULL)
{
if (!strncmp(line, "Uid:", 4))
{
uid = _parse_line(line);
break;
}
}
fclose(f);
Edi_Proc_Stats *p = calloc(1, sizeof(Edi_Proc_Stats));
p->pid = pid;
p->ppid = ppid;
p->uid = uid;
p->cpu_id = psr;
snprintf(p->command, sizeof(p->command), "%s", program_name);
p->state = _process_state_name(state);
p->cpu_time = utime + stime;
p->mem_size = mem_size;
p->mem_rss = mem_rss * getpagesize();
p->priority = pri;
p->nice = nice;
p->numthreads = numthreads;
return p;
}
#endif
#if defined(__OpenBSD__)
Edi_Proc_Stats *
edi_process_stats_by_pid(int pid)
{
struct kinfo_proc *kp;
kvm_t *kern;
char errbuf[4096];
int count, pagesize, 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;
pagesize = getpagesize();
Edi_Proc_Stats *p = malloc(sizeof(Edi_Proc_Stats));
p->pid = kp->p_pid;
p->ppid = kp->p_ppid;
p->uid = kp->p_uid;
p->cpu_id = kp->p_cpuid;
snprintf(p->command, sizeof(p->command), "%s", kp->p_comm);
p->state = _process_state_name(kp->p_stat);
p->cpu_time = kp->p_uticks + kp->p_sticks + kp->p_iticks;
p->mem_size = (kp->p_vm_tsize * pagesize) + (kp->p_vm_dsize * pagesize) + (kp->p_vm_ssize * pagesize);
p->mem_rss = kp->p_vm_rssize * pagesize;
p->priority = kp->p_priority - PZERO;
p->nice = kp->p_nice - NZERO;
p->numthreads = -1;
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)
p->numthreads++;
}
kvm_close(kern);
return p;
}
#endif
#if defined(__MacOS__)
Edi_Proc_Stats *
edi_process_stats_by_pid(int pid)
{
struct kinfo_proc kp;
struct proc_taskallinfo taskinfo;
struct proc_workqueueinfo workqueue;
size_t size;
size = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &taskinfo, sizeof(taskinfo));
if (size != sizeof(taskinfo))
return NULL;
size = proc_pidinfo(pid, PROC_PIDWORKQUEUEINFO, 0, &workqueue, sizeof(workqueue));
if (size != sizeof(workqueue))
return NULL;
Edi_Proc_Stats *p = calloc(1, sizeof(Edi_Proc_Stats));
p->pid = pid;
p->ppid = taskinfo.pbsd.pbi_ppid;
p->uid = taskinfo.pbsd.pbi_uid;
p->cpu_id = workqueue.pwq_nthreads;
snprintf(p->command, sizeof(p->command), "%s", taskinfo.pbsd.pbi_comm);
p->cpu_time = taskinfo.ptinfo.pti_total_user + taskinfo.ptinfo.pti_total_system;
p->cpu_time /= 10000000;
p->state = _process_state_name(taskinfo.pbsd.pbi_status);
p->mem_size = taskinfo.ptinfo.pti_virtual_size;
p->mem_rss = taskinfo.ptinfo.pti_resident_size;
p->priority = taskinfo.ptinfo.pti_priority;
p->nice = taskinfo.pbsd.pbi_nice;
p->numthreads = taskinfo.ptinfo.pti_threadnum;
return p;
}
#endif
#if defined(__FreeBSD__) || defined(__DragonFly__)
Edi_Proc_Stats *
edi_process_stats_by_pid(int pid)
{
struct rusage *usage;
struct kinfo_proc kp;
int mib[4];
size_t len;
int pagesize = getpagesize();
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;
Edi_Proc_Stats *p = calloc(1, sizeof(Edi_Proc_Stats));
p->pid = kp.ki_pid;
p->ppid = kp.ki_ppid;
p->uid = kp.ki_uid;
snprintf(p->command, sizeof(p->command), "%s", kp.ki_comm);
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;
p->cpu_time /= 10000;
p->state = _process_state_name(kp.ki_stat);
p->mem_size = kp.ki_size;
p->mem_rss = kp.ki_rssize * pagesize;
p->nice = kp.ki_nice = NZERO;
p->priority = kp.ki_pri.pri_level - PZERO;
p->numthreads = kp.ki_numthreads;
return p;
}
#endif

61
src/lib/edi_process.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef __EDI_PROCESS_H__
#define __EDI_PROCESS_H__
/**
* @file
* @brief Routines for querying processes.
*/
/**
* @brief Querying Processes
* @defgroup Proc
*
* @{
*
* Query processes.
*
*/
#include <Eina.h>
#include <stdint.h>
#include <unistd.h>
#if !defined(PID_MAX)
# define PID_MAX 99999
#endif
#define CMD_NAME_MAX 8192
typedef struct _Edi_Proc_Stats
{
pid_t pid;
pid_t ppid;
uid_t uid;
int8_t nice;
int8_t priority;
int cpu_id;
int32_t numthreads;
int64_t mem_size;
int64_t mem_rss;
double cpu_usage;
char command[CMD_NAME_MAX];
const char *state;
long cpu_time;
} Edi_Proc_Stats;
/**
* Query a process for its current state.
*
* @param pid The process ID to query.
*
* @return Pointer to object containing the process information or NULL if non-existent.
*/
Edi_Proc_Stats *edi_process_stats_by_pid(int pid);
/**
* @}
*/
#endif

View File

@ -17,6 +17,8 @@ src = files([
'edi_exe.h',
'edi_path.c',
'edi_path.h',
'edi_process.c',
'edi_process.h',
'edi_private.h',
'edi_scm.c',
'edi_scm.h',