From: Anastasia Lyupa Date: Thu, 23 Apr 2015 13:29:39 +0000 (+0300) Subject: [FEATURE] set webprobe profiling X-Git-Tag: submit/tizen/20151123.110932~91 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F86%2F38686%2F16;p=kernel%2Fswap-modules.git [FEATURE] set webprobe profiling - receive libewebkit addresses for web profiling through debugfs - register required probes in webprobe module - use initializer Change-Id: Ib7511138416dc74bb55b64980860091432fb9cc9 Signed-off-by: Anastasia Lyupa --- diff --git a/webprobe/Kbuild b/webprobe/Kbuild index c79cb10..8c24f5f 100644 --- a/webprobe/Kbuild +++ b/webprobe/Kbuild @@ -1,4 +1,6 @@ EXTRA_CFLAGS := $(extra_cflags) obj-m := swap_webprobe.o -swap_webprobe-y := webprobe.o +swap_webprobe-y := webprobe.o \ + webprobe_debugfs.o \ + webprobe_prof.o diff --git a/webprobe/webprobe.c b/webprobe/webprobe.c index 5489aea..96fa006 100644 --- a/webprobe/webprobe.c +++ b/webprobe/webprobe.c @@ -31,11 +31,21 @@ #include #include #include +#include #include #include #include #include #include +#include + +#include "webprobe_debugfs.h" +#include "webprobe_prof.h" + + +static unsigned long inspserver_addr_local; +static unsigned long willexecute_addr_local; +static unsigned long didexecute_addr_local; static int webprobe_copy(struct probe_info *dest, const struct probe_info *source) @@ -61,79 +71,84 @@ static int webprobe_register_probe(struct us_ip *ip) static void webprobe_unregister_probe(struct us_ip *ip, int disarm) { + if (ip->orig_addr == inspserver_addr_local) + web_func_inst_remove(web_prof_addr(INSPSERVER_START)); + else if (ip->orig_addr == willexecute_addr_local) + web_func_inst_remove(web_prof_addr(WILL_EXECUTE)); + else if (ip->orig_addr == didexecute_addr_local) + web_func_inst_remove(web_prof_addr(DID_EXECUTE)); + __swap_unregister_uretprobe(&ip->retprobe, disarm); } -static int entry_web_handler(struct uretprobe_instance *ri, struct pt_regs *regs) +static int web_entry_handler(struct uretprobe_instance *ri, + struct pt_regs *regs) { struct uretprobe *rp = ri->rp; - - if (rp && get_quiet() == QT_OFF) { - struct us_ip *ip = container_of(rp, struct us_ip, retprobe); - unsigned long addr = (unsigned long)ip->orig_addr; - - entry_web_event(addr, regs); + struct us_ip *ip; + unsigned long vaddr, page_vaddr; + struct vm_area_struct *vma; + + if (rp == NULL) + return 0; + + ip = container_of(rp, struct us_ip, retprobe); + vaddr = (unsigned long)ip->orig_addr; + page_vaddr = vaddr & PAGE_MASK; + + vma = find_vma_intersection(current->mm, page_vaddr, page_vaddr + 1); + if (vma && check_vma(vma)) { + unsigned long addr = vaddr - vma->vm_start; + struct dentry *d = vma->vm_file->f_dentry; + + if (addr == web_prof_addr(WILL_EXECUTE) && + d == web_prof_lib_dentry()) { + willexecute_addr_local = ip->orig_addr; + return entry_web_event(addr, regs); + } else if (addr == web_prof_addr(DID_EXECUTE) && + d == web_prof_lib_dentry()) { + didexecute_addr_local = ip->orig_addr; + return exit_web_event(addr, regs); + } } return 0; } -static int exit_web_handler(struct uretprobe_instance *ri, struct pt_regs *regs) + +static int web_ret_handler(struct uretprobe_instance *ri, struct pt_regs *regs) { struct uretprobe *rp = ri->rp; - - if (rp && get_quiet() == QT_OFF) { - struct us_ip *ip = container_of(rp, struct us_ip, retprobe); - unsigned long addr = (unsigned long)ip->orig_addr; - - exit_web_event(addr, regs); + struct us_ip *ip; + unsigned long vaddr, page_vaddr; + struct vm_area_struct *vma; + + if (rp == NULL) + return 0; + + ip = container_of(rp, struct us_ip, retprobe); + vaddr = (unsigned long)ip->orig_addr; + page_vaddr = vaddr & PAGE_MASK; + + vma = find_vma_intersection(current->mm, page_vaddr, page_vaddr + 1); + if (vma && check_vma(vma)) { + unsigned long addr = vaddr - vma->vm_start; + struct dentry *d = vma->vm_file->f_dentry; + + if (addr == web_prof_addr(INSPSERVER_START) && + d == web_prof_lib_dentry()) { + inspserver_addr_local = ip->orig_addr; + set_wrt_launcher_port((int)regs_return_value(regs)); + } } return 0; } -static int ret_web_handler(struct uretprobe_instance *ri, struct pt_regs *regs) -{ - set_wrt_launcher_port((int)regs_return_value(regs)); - - return 0; -} - static void webprobe_init(struct us_ip *ip) { - enum { - web_func_inspservstart, - web_func_willexecute, - web_func_didexecute - }; - static int fnum = web_func_inspservstart; - - /* FIXME: probes can be set more than once */ - switch(fnum) { - case web_func_inspservstart: - ip->retprobe.entry_handler = NULL; - ip->retprobe.handler = ret_web_handler; - fnum = web_func_willexecute; - printk("SWAP_WEBPROBE: web function ewk_view_inspector_server_start\n"); - break; - case web_func_willexecute: - /* TODO: use uprobe instead of uretprobe */ - ip->retprobe.entry_handler = entry_web_handler; - ip->retprobe.handler = NULL; - fnum = web_func_didexecute; - printk("SWAP_WEBPROBE: web function willExecute\n"); - break; - case web_func_didexecute: - /* TODO: use uprobe instead of uretprobe */ - ip->retprobe.entry_handler = exit_web_handler; - ip->retprobe.handler = NULL; - fnum = web_func_inspservstart; - printk("SWAP_WEBPROBE: web function didExecute\n"); - break; - default: - printk("SWAP_WEBPROBE: web functions more than necessary\n"); - } - + ip->retprobe.entry_handler = web_entry_handler; + ip->retprobe.handler = web_ret_handler; ip->retprobe.maxactive = 0; } @@ -153,19 +168,26 @@ static struct probe_iface webprobe_iface = { .cleanup = webprobe_cleanup }; -static int __init webprobe_module_init(void) +static int webprobe_module_init(void) { - return swap_register_probe_type(SWAP_WEBPROBE, &webprobe_iface); + int ret = 0; + + ret = swap_register_probe_type(SWAP_WEBPROBE, &webprobe_iface); + if (ret) + pr_err("Cannot register probe type SWAP_WEBPROBE\n"); + + return ret; } -static void __exit webprobe_module_exit(void) +static void webprobe_module_exit(void) { swap_unregister_probe_type(SWAP_WEBPROBE); } -module_init(webprobe_module_init); -module_exit(webprobe_module_exit); +SWAP_LIGHT_INIT_MODULE(NULL, webprobe_module_init, webprobe_module_exit, + webprobe_debugfs_init, webprobe_debugfs_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("SWAP webprobe"); -MODULE_AUTHOR("Ruslan Soloviev "); +MODULE_AUTHOR("Ruslan Soloviev " + "Anastasia Lyupa "); diff --git a/webprobe/webprobe_debugfs.c b/webprobe/webprobe_debugfs.c new file mode 100644 index 0000000..d72c4d5 --- /dev/null +++ b/webprobe/webprobe_debugfs.c @@ -0,0 +1,224 @@ +/** + * webprobe/webprobe_debugfs.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2015 + * + * 2015 Anastasia Lyupa + * + */ + + +#include +#include +#include +#include + +#include +#include + +#include "webprobe_debugfs.h" +#include "webprobe_prof.h" + +static const char ENABLED_FILE[] = "enabled"; +static const char APP_INFO_FILE[] = "app_info"; +static const char INSPSERVER_START_FILE[] = "inspector_server_start"; +static const char WILL_EXECUTE_FILE[] = "will_execute"; +static const char DID_EXECUTE_FILE[] = "did_execute"; + +enum { max_count = 256 }; +static char app_info[max_count]; + +/* ============================================================================ + * === DEBUGFS FOR WEBPROBE INSTRUMENTATION === + * ============================================================================ + */ + +static ssize_t read_enabled(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[2]; + + buf[0] = web_prof_enabled() == PROF_ON ? '1' : '0'; + buf[1] = '\n'; + + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t write_enabled(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + int ret = 0; + char buf[32]; + size_t buf_size; + + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + buf[buf_size] = '\0'; + switch (buf[0]) { + case '1': + ret = web_prof_enable(); + break; + case '0': + ret = web_prof_disable(); + break; + default: + return -EINVAL; + } + + if (ret) + return ret; + + return count; +} + +static const struct file_operations fops_enabled = { + .write = write_enabled, + .read = read_enabled, + .open = swap_init_simple_open, + .release = swap_init_simple_release, +}; + +static ssize_t write_app_info(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + int ret; + char *buf, *path, *id; + int n; + + if (count > max_count) + return -ENOMEM; + + buf = kmalloc(count + 1, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + if (copy_from_user(buf, user_buf, count)) { + ret = -EFAULT; + goto free_buf; + } + + buf[count] = '\0'; + + path = kmalloc(count, GFP_KERNEL); + if (path == NULL) { + ret = -ENOMEM; + goto free_buf; + } + + id = kmalloc(count, GFP_KERNEL); + if (id == NULL) { + ret = -ENOMEM; + goto free_path; + } + + n = sscanf(buf, "%s %s", path, id); + + if (n != 2) { + ret = -EINVAL; + goto free_app_info; + } + + web_prof_data_set(path, id); + sprintf(app_info, "%s\n", buf); + +free_app_info: + kfree(id); +free_path: + kfree(path); +free_buf: + kfree(buf); + + return ret ? ret : count; +} + +static ssize_t read_app_info(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + return simple_read_from_buffer(userbuf, count, ppos, app_info, + sizeof(app_info) - 1); +} + +static const struct file_operations fops_app_info = { + .write = write_app_info, + .read = read_app_info, + .open = swap_init_simple_open, + .release = swap_init_simple_release, +}; + + +/* ============================================================================ + * === INIT/EXIT === + * ============================================================================ + */ + +static struct dentry *webprobe_dir; + +void webprobe_debugfs_exit(void) +{ + web_prof_exit(); + + debugfs_remove_recursive(webprobe_dir); + + webprobe_dir = NULL; +} + +int webprobe_debugfs_init(void) +{ + struct dentry *dentry; + + if (web_prof_init()) + goto fail; + + dentry = swap_debugfs_getdir(); + if (dentry == NULL) + return -ENOENT; + + webprobe_dir = debugfs_create_dir("webprobe", dentry); + if (webprobe_dir == NULL) + return -ENOMEM; + + dentry = debugfs_create_file(ENABLED_FILE, 0600, webprobe_dir, NULL, + &fops_enabled); + + dentry = debugfs_create_file(APP_INFO_FILE, 0600, webprobe_dir, + NULL, &fops_app_info); + if (dentry == NULL) + goto fail; + + dentry = debugfs_create_x64(INSPSERVER_START_FILE, 0600, webprobe_dir, + web_prof_addr_ptr(INSPSERVER_START)); + if (dentry == NULL) + goto fail; + + dentry = debugfs_create_x64(WILL_EXECUTE_FILE, 0600, webprobe_dir, + web_prof_addr_ptr(WILL_EXECUTE)); + if (dentry == NULL) + goto fail; + + dentry = debugfs_create_x64(DID_EXECUTE_FILE, 0600, webprobe_dir, + web_prof_addr_ptr(DID_EXECUTE)); + if (dentry == NULL) + goto fail; + + return 0; + +fail: + webprobe_debugfs_exit(); + return -ENOMEM; +} diff --git a/webprobe/webprobe_debugfs.h b/webprobe/webprobe_debugfs.h new file mode 100644 index 0000000..beebaf9 --- /dev/null +++ b/webprobe/webprobe_debugfs.h @@ -0,0 +1,35 @@ +#ifndef _WEBPROBE_DEBUGFS_H +#define _WEBPROBE_DEBUGFS_H + +/** + * @file webprobe/webprobe_debugfs.h + * @author Anastasia Lyupa + * + * @section LICENSE + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * @section COPYRIGHT + * Copyright (C) Samsung Electronics, 2015 + * + * @section DESCRIPTION + * Debugfs for webprobe + */ + + +int webprobe_debugfs_init(void); +void webprobe_debugfs_exit(void); + + +#endif /* _WEBPROBE_DEBUGFS_H */ diff --git a/webprobe/webprobe_prof.c b/webprobe/webprobe_prof.c new file mode 100644 index 0000000..f105def --- /dev/null +++ b/webprobe/webprobe_prof.c @@ -0,0 +1,230 @@ +/** + * webprobe/webprobe_prof.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2015 + * + * 2015 Anastasia Lyupa + * + */ + + +#include +#include +#include +#include + +#include +#include + +#include "webprobe_prof.h" + + +static DEFINE_MUTEX(mutex_enable); + +struct web_prof_data { + struct dentry *app_dentry; + struct dentry *lib_dentry; + struct pf_group *pfg; + unsigned long inspserver_addr; + unsigned long willexecute_addr; + unsigned long didexecute_addr; + enum web_prof_state_t enabled; +}; + +static const char *LIBEWEBKIT2_PATH = "/usr/lib/libewebkit2.so.0"; +static struct web_prof_data *web_data; + +static struct dentry *dentry_by_path(const char *path) +{ + struct dentry *dentry; + struct path st_path; + if (kern_path(path, LOOKUP_FOLLOW, &st_path) != 0) { + pr_err("failed to lookup dentry for path %s!\n", path); + return NULL; + } + dentry = st_path.dentry; + path_put(&st_path); + + return dentry; +} + +u64 *web_prof_addr_ptr(enum web_prof_addr_t type) +{ + u64 *addr_ptr; + + switch (type) { + case INSPSERVER_START: + addr_ptr = (u64 *)&web_data->inspserver_addr; + break; + case WILL_EXECUTE: + addr_ptr = (u64 *)&web_data->willexecute_addr; + break; + case DID_EXECUTE: + addr_ptr = (u64 *)&web_data->didexecute_addr; + break; + default: + pr_err("ERROR: WEB_PROF_ADDR_PTR_TYPE=0x%x\n", type); + addr_ptr = NULL; + } + + return addr_ptr; +} + +unsigned long web_prof_addr(enum web_prof_addr_t type) +{ + unsigned long addr; + + switch (type) { + case INSPSERVER_START: + addr = web_data->inspserver_addr; + break; + case WILL_EXECUTE: + addr = web_data->willexecute_addr; + break; + case DID_EXECUTE: + addr = web_data->didexecute_addr; + break; + default: + pr_err("ERROR: WEB_PROF_ADDR_TYPE=0x%x\n", type); + addr = 0; + } + + return addr; +} + +static int web_func_inst_add(unsigned long addr) +{ + int ret; + struct probe_info probe; + + probe.probe_type = SWAP_WEBPROBE; + probe.size = 0; + + ret = pf_register_probe(web_data->pfg, web_data->lib_dentry, + addr, &probe); + + return ret; +} + +int web_func_inst_remove(unsigned long addr) +{ + int ret; + + /* FIXME: check that address needs removing */ + ret = pf_unregister_probe(web_data->pfg, web_data->lib_dentry, + addr); + + return ret; +} + +int web_prof_data_set(char *app_path, char *app_id) +{ + web_data->app_dentry = dentry_by_path(app_path); + if (web_data->app_dentry == NULL) + return -EFAULT; + + web_data->lib_dentry = dentry_by_path(LIBEWEBKIT2_PATH); + if (web_data->lib_dentry == NULL) + return -EFAULT; + + web_data->pfg = get_pf_group_by_comm(app_id, web_data->app_dentry); + if (web_data->pfg == NULL) + return -EFAULT; + + return 0; +} + +struct dentry *web_prof_lib_dentry(void) +{ + return web_data->lib_dentry; +} + +enum web_prof_state_t web_prof_enabled(void) +{ + return web_data->enabled; +} + +int web_prof_enable(void) +{ + int ret = 0; + + mutex_lock(&mutex_enable); + if (web_data->enabled == PROF_OFF) { + web_data->enabled = PROF_ON; + + if ((web_data->inspserver_addr == 0) || + (web_data->willexecute_addr == 0) || + (web_data->didexecute_addr == 0)) { + pr_err("ERROR: Can't enable web profiling\n"); + ret = -EFAULT; + } else { + web_func_inst_add(web_data->inspserver_addr); + web_func_inst_add(web_data->willexecute_addr); + web_func_inst_add(web_data->didexecute_addr); + } + } else { + pr_err("ERROR: Web profiling is already enabled\n"); + } + mutex_unlock(&mutex_enable); + + return ret; +} + +int web_prof_disable(void) +{ + int ret = 0; + + mutex_lock(&mutex_enable); + if (web_data->enabled == PROF_ON) { + web_data->enabled = PROF_OFF; + + if ((web_data->inspserver_addr == 0) || + (web_data->willexecute_addr == 0) || + (web_data->didexecute_addr == 0)) { + pr_err("ERROR: Can't disable web profiling\n"); + ret = -EFAULT; + } else { + web_func_inst_remove(web_data->inspserver_addr); + web_func_inst_remove(web_data->willexecute_addr); + web_func_inst_remove(web_data->didexecute_addr); + } + } else { + pr_err("ERROR: Web profiling is already disabled\n"); + } + mutex_unlock(&mutex_enable); + + return ret; +} + +int web_prof_init(void) +{ + web_data = kmalloc(sizeof(*web_data), GFP_KERNEL); + if (web_data == NULL) + return -ENOMEM; + + memset(web_data, 0, sizeof(struct web_prof_data)); + + web_data->enabled = PROF_OFF; + + return 0; +} + + +void web_prof_exit(void) +{ + kfree(web_data); +} diff --git a/webprobe/webprobe_prof.h b/webprobe/webprobe_prof.h new file mode 100644 index 0000000..d0144f9 --- /dev/null +++ b/webprobe/webprobe_prof.h @@ -0,0 +1,53 @@ +#ifndef _WEBPROBE_PROF_H +#define _WEBPROBE_PROF_H + +/** + * @file webprobe/webprobe_prof.h + * @author Anastasia Lyupa + * + * @section LICENSE + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * @section COPYRIGHT + * Copyright (C) Samsung Electronics, 2015 + * + * @section DESCRIPTION + * Profiling for webprobe + */ + + +enum web_prof_addr_t { + INSPSERVER_START = 1, + WILL_EXECUTE = 2, + DID_EXECUTE = 3 +}; + +enum web_prof_state_t { + PROF_OFF, + PROF_ON +}; + +int web_prof_init(void); +void web_prof_exit(void); +int web_prof_enable(void); +int web_prof_disable(void); +enum web_prof_state_t web_prof_enabled(void); +int web_func_inst_remove(unsigned long addr); +u64 *web_prof_addr_ptr(enum web_prof_addr_t type); +unsigned long web_prof_addr(enum web_prof_addr_t type); +int web_prof_data_set(char *app_path, char *app_id); +struct dentry *web_prof_lib_dentry(void); + +#endif /* _WEBPROBE_PROF_H */