summaryrefslogtreecommitdiff
path: root/src/proc-scanner.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/proc-scanner.c')
-rw-r--r--src/proc-scanner.c218
1 files changed, 218 insertions, 0 deletions
diff --git a/src/proc-scanner.c b/src/proc-scanner.c
new file mode 100644
index 0000000..4e72753
--- /dev/null
+++ b/src/proc-scanner.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Flora License, Version 1.1 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include "proc-scanner.h"
+#include "procfs.h"
+#include "process.h"
+#include "err-check.h"
+#include "clock.h"
+
+#define SWAP(a, b, type) do { type tmp = (a); (a) = (b); (b) = tmp; } while(0);
+#define PROCTAB_INITIAL_LEN 64
+
+struct proctab {
+ struct process *processes;
+ int size;
+ int max;
+};
+
+struct proc_scanner {
+ struct proctab current;
+ struct proctab history;
+ int last_history_index;
+};
+
+void proctab_free(struct proctab *tab)
+{
+ if (!tab) return;
+ for (int i = 0; i < tab->size; i++)
+ process_shutdown(&tab->processes[i]);
+ if (tab->processes) free(tab->processes);
+}
+
+void proctab_resize(struct proctab *tab, int new_max)
+{
+ tab->processes = realloc(tab->processes, new_max * sizeof(struct process));
+ if (!tab->processes) abort();
+ tab->max = new_max;
+}
+
+void proctab_append(struct proctab *tab, struct process *proc)
+{
+ if (tab->size >= tab->max)
+ proctab_resize(tab, tab->max *= 2);
+ tab->processes[tab->size++] = *proc;
+}
+
+void proctab_reset(struct proctab *tab)
+{
+ for (int i = 0; i < tab->size; i++)
+ process_shutdown(&tab->processes[i]);
+ tab->size = 0;
+}
+
+proc_scanner_t *proc_scanner_new()
+{
+ proc_scanner_t *ret = calloc(1, sizeof(proc_scanner_t));
+ if (!ret) {
+ return NULL;
+ }
+ proctab_resize(&ret->current, PROCTAB_INITIAL_LEN);
+ proctab_resize(&ret->history, PROCTAB_INITIAL_LEN);
+ return ret;
+}
+
+void proc_scanner_free(proc_scanner_t *scanner)
+{
+ if (!scanner) return;
+ proctab_free(&scanner->current);
+ proctab_free(&scanner->history);
+ free(scanner);
+}
+
+static int _sort_processes_by_pid(const void *a, const void *b)
+{
+ const struct process *proc1 = a;
+ const struct process *proc2 = b;
+ return process_get_pid(proc1) - process_get_pid(proc2);
+}
+
+static int _search_for_pid_key(const void *a, const void *b)
+{
+ const int *key = a;
+ const struct process *proc = b;
+ return *key - proc->pid;
+}
+
+static int _sort_pids(const void *a, const void *b)
+{
+ const int *pid1 = a;
+ const int *pid2 = a;
+ return pid1 - pid2;
+}
+
+// Search for process entry in history
+static struct process *_proc_scanner_find_process_in_history(proc_scanner_t *scanner, int pid)
+{
+ if (scanner->history.size == 0)
+ return NULL;
+
+ // the scanner->history_last_history_index helps for quickly find
+ // history entry matching given pid. We assume that history is sorted ascending by pid
+ // and searching for pid is done in same order.
+ // the speedup gain is about ~15% compared to bsearch only.
+ while (scanner->last_history_index < scanner->history.size)
+ {
+ if (scanner->history.processes[scanner->last_history_index].pid < pid)
+ {
+ scanner->last_history_index++;
+ continue;
+ }
+ if (scanner->history.processes[scanner->last_history_index].pid == pid) {
+ return &scanner->history.processes[scanner->last_history_index++];
+ }
+ break;
+ }
+
+ return bsearch(&pid, scanner->history.processes, scanner->history.size, sizeof(struct process), _search_for_pid_key);
+}
+
+static bool _proc_scanner_read_pid(int pid, void *user_data)
+{
+ proc_scanner_t *scanner = user_data;
+ struct process proc_new;
+
+ struct process *proc = _proc_scanner_find_process_in_history(scanner, pid);
+
+ if (!proc) {
+ process_init(pid, &proc_new);
+ proc = &proc_new;
+ } else {
+ process_move(&proc_new, proc);
+ }
+
+ if (process_update(&proc_new) != 0) {
+ process_shutdown(&proc_new);
+ return true;
+ }
+ proctab_append(&scanner->current, &proc_new);
+
+ return true;
+}
+
+static int _proc_scanner_iterate_array(int *pids, int n_pids, procfs_pid_iterator_cb cb, void *user_data)
+{
+ qsort(pids, n_pids, sizeof(int), _sort_pids);
+
+ for (int i = 0; i < n_pids; ++i) {
+ if (!cb(pids[i], user_data))
+ break;
+ }
+ return 0;
+}
+
+static int _proc_scanner_scan_internal(proc_scanner_t *scanner, int *pids, int n_pids)
+{
+ ON_NULL_RETURN_VAL(scanner, -1);
+
+ // swap processes and history
+ SWAP(scanner->current, scanner->history, struct proctab);
+
+ // reset current tab, so we can start adding results of current scan
+ proctab_reset(&scanner->current);
+
+ // prepare history
+ scanner->last_history_index = 0;
+ qsort(scanner->history.processes, scanner->history.size, sizeof(struct process), _sort_processes_by_pid);
+
+ if (pids)
+ return _proc_scanner_iterate_array(pids, n_pids, _proc_scanner_read_pid, scanner);
+ else
+ return procfs_iterate_pids(_proc_scanner_read_pid, scanner);
+}
+
+int proc_scanner_scan(proc_scanner_t *scanner)
+{
+ return _proc_scanner_scan_internal(scanner, NULL, -1);
+}
+int proc_scanner_scan_pids(proc_scanner_t *scanner, int *pids, int n_pids)
+{
+ return _proc_scanner_scan_internal(scanner, pids, n_pids);
+}
+
+int proc_scanner_foreach_process(proc_scanner_t *scanner, foreach_t cb, void *data)
+{
+ ON_NULL_RETURN_VAL(scanner, -1);
+ ON_NULL_RETURN_VAL(cb, -1);
+
+ for (int i = 0; i < scanner->current.size; ++i) {
+ if (!cb(&scanner->current.processes[i], data))
+ break;
+ }
+ return 0;
+}
+
+int proc_scanner_sort_processes(proc_scanner_t *scanner, sort_t cb)
+{
+ ON_NULL_RETURN_VAL(scanner, -1);
+ ON_NULL_RETURN_VAL(cb, -1);
+
+ qsort(scanner->current.processes, scanner->current.size, sizeof(struct process), (__compar_fn_t)cb);
+
+ return 0;
+}