diff options
Diffstat (limited to 'src/proc-scanner.c')
-rw-r--r-- | src/proc-scanner.c | 218 |
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; +} |