summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuslan Soloviev <r.soloviev@samsung.com>2014-11-27 10:12:42 +0300
committerDmitry Kovalenko <d.kovalenko@samsung.com>2014-12-03 06:52:07 -0800
commit4f2786f1a867477f9083641e96c379514846bd3c (patch)
treea379b51c657c9b76688259f354cda992299c2d2f
parent5a1b2e6e67e0be4897c9527247cdfb7a11244675 (diff)
downloadswap-modules-4f2786f1a867477f9083641e96c379514846bd3c.tar.gz
swap-modules-4f2786f1a867477f9083641e96c379514846bd3c.tar.bz2
swap-modules-4f2786f1a867477f9083641e96c379514846bd3c.zip
[FEATURE] Add web profiling
Setup probes (probe type 3): ewk_context_inspector_server_start JSC::ProfileGenerator::willExecute JSC::ProfileGenerator::didExecute Change-Id: I65534ea544685b55d805df910d85b6a31eae2a93 Signed-off-by: Ruslan Soloviev <r.soloviev@samsung.com>
-rw-r--r--Kbuild3
-rwxr-xr-xbuild.sh5
-rwxr-xr-xpackaging/swap-modules.spec2
-rw-r--r--parser/msg_cmd.c28
-rw-r--r--parser/msg_cmd.h2
-rw-r--r--parser/msg_parser.c21
-rw-r--r--parser/swap_msg_parser.c25
-rw-r--r--us_manager/probes/probes.h1
-rw-r--r--webprobe/Kbuild4
-rw-r--r--webprobe/webprobe.c168
-rw-r--r--writer/swap_writer_module.c146
-rw-r--r--writer/swap_writer_module.h3
12 files changed, 395 insertions, 13 deletions
diff --git a/Kbuild b/Kbuild
index 40b8f272..54f3c3ec 100644
--- a/Kbuild
+++ b/Kbuild
@@ -12,4 +12,5 @@ obj-m := buffer/ \
sampler/ \
energy/ \
parser/ \
- retprobe/
+ retprobe/ \
+ webprobe/
diff --git a/build.sh b/build.sh
index e8244007..3d21d976 100755
--- a/build.sh
+++ b/build.sh
@@ -35,6 +35,7 @@ sampler_dir=${modules_dir}/sampler
energy_dir=${modules_dir}/energy
parser_dir=${modules_dir}/parser
retprobe_dir=${modules_dir}/retprobe
+webprobe_dir=${modules_dir}/webprobe
buffer_module_name=swap_buffer.ko
driver_module_name=swap_driver.ko
@@ -49,6 +50,7 @@ energy_module_name=swap_energy.ko
parser_module_name=swap_message_parser.ko
ksyms_module_name=swap_ksyms.ko
retprobe_module_name=swap_retprobe.ko
+webprobe_module_name=swap_webprobe.ko
install_dir="/opt/swap/sdk"
@@ -72,7 +74,8 @@ ${sampler_dir}/${sampler_module_name} \
${energy_dir}/${energy_module_name} \
${parser_dir}/${parser_module_name} \
${ksyms_dir}/${ksyms_module_name} \
-${retprobe_dir}/${retprobe_module_name}"
+${retprobe_dir}/${retprobe_module_name} \
+${webprobe_dir}/${webprobe_module_name}"
for m in ${modules} ; do
${cross_compile}strip -x -g $m
diff --git a/packaging/swap-modules.spec b/packaging/swap-modules.spec
index 7d2ad40e..9a2c0306 100755
--- a/packaging/swap-modules.spec
+++ b/packaging/swap-modules.spec
@@ -53,6 +53,7 @@ install -m 666 sampler/swap_sampler.ko -t %{buildroot}/opt/swap/sdk
install -m 666 energy/swap_energy.ko -t %{buildroot}/opt/swap/sdk
install -m 666 parser/swap_message_parser.ko -t %{buildroot}/opt/swap/sdk
install -m 666 retprobe/swap_retprobe.ko -t %{buildroot}/opt/swap/sdk
+install -m 666 webprobe/swap_webprobe.ko -t %{buildroot}/opt/swap/sdk
%files
%defattr(-,root,root)
@@ -69,3 +70,4 @@ install -m 666 retprobe/swap_retprobe.ko -t %{buildroot}/opt/swap/sdk
/opt/swap/sdk/swap_energy.ko
/opt/swap/sdk/swap_message_parser.ko
/opt/swap/sdk/swap_retprobe.ko
+/opt/swap/sdk/swap_webprobe.ko
diff --git a/parser/msg_cmd.c b/parser/msg_cmd.c
index b516a217..fa890f92 100644
--- a/parser/msg_cmd.c
+++ b/parser/msg_cmd.c
@@ -29,6 +29,7 @@
#include <linux/errno.h>
+#include <linux/delay.h>
#include <writer/swap_writer_module.h>
#include "msg_parser.h"
#include "msg_buf.h"
@@ -37,6 +38,8 @@
#include "us_inst.h"
#include <us_manager/us_manager.h>
+static int wrt_launcher_port = 0;
+
static int set_config(struct conf_data *conf)
{
int ret;
@@ -237,6 +240,31 @@ free_us_inst:
return ret;
}
+void set_wrt_launcher_port(int port)
+{
+ wrt_launcher_port = port;
+}
+EXPORT_SYMBOL_GPL(set_wrt_launcher_port);
+
+#define GET_PORT_DELAY 100 /* msec */
+#define GET_PORT_TIMEOUT 10000 /* msec */
+
+int get_wrt_launcher_port(void)
+{
+ int port;
+ int timeout = GET_PORT_TIMEOUT;
+
+ do {
+ port = wrt_launcher_port;
+ timeout -= GET_PORT_DELAY;
+ mdelay(GET_PORT_DELAY);
+ } while (!port && timeout > 0);
+
+ set_wrt_launcher_port(0);
+
+ return port;
+}
+
/**
* @brief Initializes commands handling.
*
diff --git a/parser/msg_cmd.h b/parser/msg_cmd.h
index 16132ca6..e4449979 100644
--- a/parser/msg_cmd.h
+++ b/parser/msg_cmd.h
@@ -41,5 +41,7 @@ int msg_stop(struct msg_buf *mb);
int msg_config(struct msg_buf *mb);
int msg_swap_inst_add(struct msg_buf *mb);
int msg_swap_inst_remove(struct msg_buf *mb);
+int get_wrt_launcher_port(void);
+void set_wrt_launcher_port(int port);
#endif /* _MSG_CMD_H */
diff --git a/parser/msg_parser.c b/parser/msg_parser.c
index 63315a60..6e128865 100644
--- a/parser/msg_parser.c
+++ b/parser/msg_parser.c
@@ -304,6 +304,21 @@ free_args:
}
/**
+ * @brief Gets webprobe data and puts it to the probe_info struct.
+ *
+ * @param mb Pointer to the message buffer.
+ * @param pi Pointer to the probe_info struct.
+ * @return 0 on success, error code on error.
+ */
+int get_webprobe(struct msg_buf *mb, struct probe_info *pi)
+{
+ pi->probe_type = SWAP_WEBPROBE;
+ pi->size = 0;
+
+ return 0;
+}
+
+/**
* @brief Retprobe data cleanup.
*
* @param pi Pointer to the probe_info comprising retprobe.
@@ -361,6 +376,10 @@ struct func_inst_data *create_func_inst_data(struct msg_buf *mb)
if (get_retprobe(mb, &(fi->probe_i)) != 0)
goto free_func_inst;
break;
+ case SWAP_WEBPROBE:
+ if (get_webprobe(mb, &(fi->probe_i)) != 0)
+ goto free_func_inst;
+ break;
default:
printk(KERN_WARNING "SWAP PARSER: Wrong probe type %d!\n", type);
goto free_func_inst;
@@ -386,6 +405,8 @@ void destroy_func_inst_data(struct func_inst_data *fi)
case SWAP_RETPROBE:
put_retprobe(&(fi->probe_i));
break;
+ case SWAP_WEBPROBE:
+ break;
default:
printk(KERN_WARNING "SWAP PARSER: Wrong probe type %d!\n",
fi->probe_i.probe_type);
diff --git a/parser/swap_msg_parser.c b/parser/swap_msg_parser.c
index f1071e25..dca29999 100644
--- a/parser/swap_msg_parser.c
+++ b/parser/swap_msg_parser.c
@@ -47,12 +47,13 @@
* @brief Message IDs.
*/
enum MSG_ID {
- MSG_KEEP_ALIVE = 0x0001, /**< Keep alive message. */
- MSG_START = 0x0002, /**< Start message. */
- MSG_STOP = 0x0003, /**< Stop message. */
- MSG_CONFIG = 0x0004, /**< Config message. */
- MSG_SWAP_INST_ADD = 0x0008, /**< Swap inst add message. */
- MSG_SWAP_INST_REMOVE = 0x0009 /**< Swap inst remove message. */
+ MSG_KEEP_ALIVE = 0x0001, /**< Keep alive message. */
+ MSG_START = 0x0002, /**< Start message. */
+ MSG_STOP = 0x0003, /**< Stop message. */
+ MSG_CONFIG = 0x0004, /**< Config message. */
+ MSG_SWAP_INST_ADD = 0x0008, /**< Swap inst add message. */
+ MSG_SWAP_INST_REMOVE = 0x0009, /**< Swap inst remove message. */
+ MSG_WRT_LAUNCHER_PORT = 0x8001 /**< WRT launcher port. */
};
/**
@@ -128,6 +129,18 @@ static int msg_handler(void __user *msg)
print_parse_debug("MSG_SWAP_INST_REMOVE. size=%d\n", size);
ret = msg_swap_inst_remove(&mb);
break;
+ case MSG_WRT_LAUNCHER_PORT: {
+ /* TODO: discuss wrt-launcher port transfer */
+ int port;
+ print_parse_debug("MSG_WRT_LAUNCHER_PORT. size=%d\n", size);
+ port = get_wrt_launcher_port();
+ if (copy_to_user(payload, &port, sizeof(port))) {
+ ret = -EIO;
+ break;
+ }
+ ret = port ? 0 : -EINVAL;
+ break;
+ }
default:
print_err("incorrect message ID [%u]. size=%d\n", msg_id, size);
ret = -EINVAL;
diff --git a/us_manager/probes/probes.h b/us_manager/probes/probes.h
index 4ba81112..d83f1cb5 100644
--- a/us_manager/probes/probes.h
+++ b/us_manager/probes/probes.h
@@ -37,6 +37,7 @@
*/
enum probe_t {
SWAP_RETPROBE = 0, /* Retprobe */
+ SWAP_WEBPROBE = 3, /* Webprobe */
SWAP_PROBE_MAX_VAL /* Probes max value. */
};
diff --git a/webprobe/Kbuild b/webprobe/Kbuild
new file mode 100644
index 00000000..c79cb105
--- /dev/null
+++ b/webprobe/Kbuild
@@ -0,0 +1,4 @@
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := swap_webprobe.o
+swap_webprobe-y := webprobe.o
diff --git a/webprobe/webprobe.c b/webprobe/webprobe.c
new file mode 100644
index 00000000..d4bc268c
--- /dev/null
+++ b/webprobe/webprobe.c
@@ -0,0 +1,168 @@
+/**
+ * webprobe/webprobe.c
+ * @author Ruslan Soloviev
+ *
+ * @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, 2014
+ *
+ * @section DESCRIPTION
+ *
+ * Web application profiling
+ */
+
+
+#include <us_manager/us_manager.h>
+#include <us_manager/sspt/ip.h>
+#include <us_manager/probes/register_probes.h>
+#include <writer/swap_writer_module.h>
+#include <uprobe/swap_uprobes.h>
+#include <parser/msg_cmd.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+static int webprobe_copy(struct probe_info *dest,
+ const struct probe_info *source)
+{
+ memcpy(dest, source, sizeof(*source));
+
+ return 0;
+}
+
+static void webprobe_cleanup(struct probe_info *probe_i)
+{
+}
+
+static struct uprobe *webprobe_get_uprobe(struct us_ip *ip)
+{
+ return &ip->retprobe.up;
+}
+
+static int webprobe_register_probe(struct us_ip *ip)
+{
+ return swap_register_uretprobe(&ip->retprobe);
+}
+
+static void webprobe_unregister_probe(struct us_ip *ip, int disarm)
+{
+ __swap_unregister_uretprobe(&ip->retprobe, disarm);
+}
+
+static int entry_web_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);
+ }
+
+ return 0;
+}
+
+static int exit_web_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);
+ }
+
+ 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;
+}
+
+#define WEB_FUNC_INSPSERVSTART 0
+#define WEB_FUNC_WILLEXECUTE 1
+#define WEB_FUNC_DIDEXECUTE 2
+
+static void webprobe_init(struct us_ip *ip)
+{
+ static int fcnt = 0;
+
+ switch(fcnt++) {
+ case WEB_FUNC_INSPSERVSTART:
+ ip->retprobe.entry_handler = NULL;
+ ip->retprobe.handler = ret_web_handler;
+ 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;
+ 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;
+ printk("SWAP_WEBPROBE: web function didExecute\n");
+ /* FIXME: probes can be set more than once */
+ fcnt = 0;
+ break;
+ default:
+ printk("SWAP_WEBPROBE: web functions more than necessary\n");
+ }
+
+ ip->retprobe.maxactive = 0;
+}
+
+static void webprobe_uninit(struct us_ip *ip)
+{
+ webprobe_cleanup(&ip->probe_i);
+}
+
+
+static struct probe_iface webprobe_iface = {
+ .init = webprobe_init,
+ .uninit = webprobe_uninit,
+ .reg = webprobe_register_probe,
+ .unreg = webprobe_unregister_probe,
+ .get_uprobe = webprobe_get_uprobe,
+ .copy = webprobe_copy,
+ .cleanup = webprobe_cleanup
+};
+
+static int __init webprobe_module_init(void)
+{
+ return swap_register_probe_type(SWAP_WEBPROBE, &webprobe_iface);
+}
+
+static void __exit webprobe_module_exit(void)
+{
+ swap_unregister_probe_type(SWAP_WEBPROBE);
+}
+
+module_init(webprobe_module_init);
+module_exit(webprobe_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SWAP webprobe");
+MODULE_AUTHOR("Ruslan Soloviev <r.soloviev@samsung.com>");
diff --git a/writer/swap_writer_module.c b/writer/swap_writer_module.c
index 00e35c72..41626281 100644
--- a/writer/swap_writer_module.c
+++ b/writer/swap_writer_module.c
@@ -70,7 +70,9 @@ enum MSG_ID {
MSG_CONTEXT_SWITCH_EXIT = 0x0011, /**< Context switch exit */
MSG_PROC_MAP = 0x0012, /**< Process map */
MSG_PROC_UNMAP = 0x0013, /**< Process unmap */
- MSG_PROC_COMM = 0x0014 /**< Process comm */
+ MSG_PROC_COMM = 0x0014, /**< Process comm */
+ MSG_WEB_FUNCTION_ENTRY = 0x0015, /**< Web function entry */
+ MSG_WEB_FUNCTION_EXIT = 0x0016 /**< Web function exit */
};
static char *cpu_buf[NR_CPUS];
@@ -1240,6 +1242,7 @@ static int pack_custom_event(char *buf, int len, const char *fmt, va_list args)
enum { max_str_len = 512 };
const char *p;
char *buf_orig = buf;
+ int len_s = 0;
for (p = fmt; *p != '\0'; p++) {
char ch = *p;
@@ -1252,6 +1255,13 @@ static int pack_custom_event(char *buf, int len, const char *fmt, va_list args)
len -= 1;
switch (ch) {
+ case '*': /* string length (without '\0') */
+ /* usage: '*s' or '*S' */
+ /* will not pack to buffer */
+ len_s = va_arg(args, int);
+ buf -= 1;
+ len += 1;
+ break;
case 'b': /* 1 byte(bool) */
if (len < 1)
return -ENOMEM;
@@ -1292,11 +1302,14 @@ static int pack_custom_event(char *buf, int len, const char *fmt, va_list args)
case 's': /* userspace string with '\0' terminating byte */
{
const char __user *str;
- int len_s, n;
+ int n;
str = va_arg(args, const char __user *);
/* strnlen_user includes '\0' in its return value */
- len_s = strnlen_user(str, max_str_len);
+ if (len_s == 0)
+ len_s = strnlen_user(str, max_str_len);
+ else
+ len_s++; /* + '\0' */
if (len < len_s)
return -ENOMEM;
/* strncpy_from_user returns the length of the copied
@@ -1308,15 +1321,16 @@ static int pack_custom_event(char *buf, int len, const char *fmt, va_list args)
buf += n + 1;
len -= n + 1;
+ len_s = 0;
break;
}
case 'S': /* kernelspace string with '\0' terminating byte */
{
const char *str;
- int len_s;
str = va_arg(args, const char *);
- len_s = strnlen(str, max_str_len);
+ if (len_s == 0)
+ len_s = strnlen(str, max_str_len);
if (len < len_s + 1) /* + '\0' */
return -ENOMEM;
strncpy(buf, str, len_s);
@@ -1324,6 +1338,7 @@ static int pack_custom_event(char *buf, int len, const char *fmt, va_list args)
buf += len_s + 1;
len -= len_s + 1;
+ len_s = 0;
break;
}
case 'a': /* userspace byte array (len + ptr) */
@@ -1374,6 +1389,18 @@ static int pack_custom_event(char *buf, int len, const char *fmt, va_list args)
return buf - buf_orig;
}
+static int pack_custom_args(char *buf, int len, const char *fmt, ...)
+{
+ va_list vargs;
+ int ret;
+
+ va_start(vargs, fmt);
+ ret = pack_custom_event(buf, len, fmt, vargs);
+ va_end(vargs);
+
+ return ret;
+}
+
enum { max_custom_event_size = 2048 };
/**
@@ -1455,6 +1482,115 @@ put_buf:
}
EXPORT_SYMBOL_GPL(custom_exit_event);
+
+
+
+/* ============================================================================
+ * = WEB APP EVENT =
+ * ============================================================================
+ */
+
+/* TODO: develop method for obtaining this data during build... */
+/* location: webkit2-efl-123997_0.11.113/Source/WTF/wtf/text/StringImpl.h:70 */
+struct MStringImpl {
+ unsigned m_refCount;
+ unsigned m_length;
+ union {
+ const unsigned char *m_data8;
+ const unsigned short *m_data16;
+ };
+ union {
+ void* m_buffer;
+ struct MStringImpl *m_substringBuffer;
+ unsigned short *m_copyData16;
+ };
+ unsigned m_hashAndFlags;
+};
+
+/* location: webkit2-efl-123997_0.11.113/Source/JavaScriptCore/profiler/
+ * CallIdentifier.h:36
+ */
+struct MCallIdentifier {
+ struct MStringImpl *m_name;
+ struct MStringImpl *m_url;
+ unsigned m_lineNumber;
+};
+
+int entry_web_event(unsigned long func_addr, struct pt_regs *regs)
+{
+ char *buf, *payload;
+ int ret, t;
+ struct MCallIdentifier *callIdentifier;
+ struct {
+ const char *str;
+ int len;
+ } m_name, m_url;
+
+ if (!check_event(current))
+ return 0;
+
+ callIdentifier = (void *)swap_get_uarg(regs, 2);
+
+ if (!(!get_user(t, (int *)&callIdentifier->m_name) &&
+ !get_user(m_name.str, &((struct MStringImpl *)t)->m_data8) &&
+ !get_user(m_name.len, &((struct MStringImpl *)t)->m_length) &&
+ !get_user(t, (int *)&callIdentifier->m_url) &&
+ !get_user(m_url.str, &((struct MStringImpl *)t)->m_data8) &&
+ !get_user(m_url.len, &((struct MStringImpl *)t)->m_length))) {
+ printk("SWAP_WRITER: cannot read user memory\n");
+ return 0;
+ }
+
+ buf = get_current_buf();
+ payload = pack_basic_msg_fmt(buf, MSG_WEB_FUNCTION_ENTRY);
+ payload = pack_msg_func_entry(payload, "ssd", func_addr, regs, PT_US,
+ PST_NONE);
+ payload += pack_custom_args(payload, 1024, "*s*sd", m_name.len,
+ m_name.str, m_url.len, m_url.str,
+ callIdentifier->m_lineNumber);
+ set_len_msg(buf, payload);
+ ret = write_to_buffer(buf);
+ put_current_buf();
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(entry_web_event);
+
+int exit_web_event(unsigned long func_addr, struct pt_regs *regs)
+{
+ char *buf, *payload;
+ int ret, t;
+ struct MCallIdentifier *callIdentifier;
+ struct {
+ const char *str;
+ int len;
+ } m_name;
+
+ if (!check_event(current))
+ return 0;
+
+ callIdentifier = (void *)swap_get_uarg(regs, 2);
+
+ if (!(!get_user(t, (int *)&callIdentifier->m_name) &&
+ !get_user(m_name.str, &((struct MStringImpl *)t)->m_data8) &&
+ !get_user(m_name.len, &((struct MStringImpl *)t)->m_length))) {
+ printk("SWAP_WRITER: cannot read user memory\n");
+ return 0;
+ }
+
+ buf = get_current_buf();
+ payload = pack_basic_msg_fmt(buf, MSG_WEB_FUNCTION_EXIT);
+ payload = pack_msg_func_entry(payload, "s", func_addr, regs, PT_US,
+ PST_NONE);
+ payload += pack_custom_args(payload, 1024, "*s", m_name.len, m_name.str);
+ set_len_msg(buf, payload);
+ ret = write_to_buffer(buf);
+ put_current_buf();
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(exit_web_event);
+
static int __init swap_writer_module_init(void)
{
int ret;
diff --git a/writer/swap_writer_module.h b/writer/swap_writer_module.h
index c975a8ea..38f22883 100644
--- a/writer/swap_writer_module.h
+++ b/writer/swap_writer_module.h
@@ -80,6 +80,9 @@ int entry_event(const char *fmt, unsigned long func_addr, struct pt_regs *regs,
int exit_event(char ret_type, struct pt_regs *regs, int pt, int sub_type,
unsigned long func_addr, unsigned long ret_addr);
+int entry_web_event(unsigned long func_addr, struct pt_regs *regs);
+int exit_web_event(unsigned long func_addr, struct pt_regs *regs);
+
int switch_entry(struct pt_regs *regs);
int switch_exit(struct pt_regs *regs);