|
|
dcf580 |
diff -up oprofile-0.9.7/daemon/init.c.xen oprofile-0.9.7/daemon/init.c
|
|
|
dcf580 |
--- oprofile-0.9.7/daemon/init.c.xen 2011-07-04 22:25:04.000000000 -0400
|
|
|
dcf580 |
+++ oprofile-0.9.7/daemon/init.c 2011-11-28 16:25:07.577000010 -0500
|
|
|
dcf580 |
@@ -312,6 +312,8 @@ static void opd_26_init(void)
|
|
|
dcf580 |
|
|
|
dcf580 |
opd_create_vmlinux(vmlinux, kernel_range);
|
|
|
dcf580 |
opd_create_xen(xenimage, xen_range);
|
|
|
dcf580 |
+ if (xen_passive_setup)
|
|
|
dcf580 |
+ opd_create_passive(xen_passive_setup);
|
|
|
dcf580 |
|
|
|
dcf580 |
opd_buf_size = opd_read_fs_int("/dev/oprofile/", "buffer_size", 1);
|
|
|
dcf580 |
kernel_pointer_size = opd_read_fs_int("/dev/oprofile/", "pointer_size", 1);
|
|
|
dcf580 |
diff -up oprofile-0.9.7/daemon/opd_kernel.c.xen oprofile-0.9.7/daemon/opd_kernel.c
|
|
|
dcf580 |
--- oprofile-0.9.7/daemon/opd_kernel.c.xen 2011-07-04 22:25:04.000000000 -0400
|
|
|
dcf580 |
+++ oprofile-0.9.7/daemon/opd_kernel.c 2011-11-28 16:25:07.579000010 -0500
|
|
|
dcf580 |
@@ -34,11 +34,22 @@ static struct kernel_image vmlinux_image
|
|
|
dcf580 |
|
|
|
dcf580 |
static struct kernel_image xen_image;
|
|
|
dcf580 |
|
|
|
dcf580 |
+static struct kernel_image xen_image_anon;
|
|
|
dcf580 |
+static struct kernel_image vmlinux_image_anon;
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+static LIST_HEAD(passive_vmlinux);
|
|
|
dcf580 |
+static LIST_HEAD(passive_xen);
|
|
|
dcf580 |
+static LIST_HEAD(passive_apps);
|
|
|
dcf580 |
+static LIST_HEAD(passive_modules);
|
|
|
dcf580 |
+static LIST_HEAD(passive_xen_anon);
|
|
|
dcf580 |
+
|
|
|
dcf580 |
void opd_create_vmlinux(char const * name, char const * arg)
|
|
|
dcf580 |
{
|
|
|
dcf580 |
/* vmlinux is *not* on the list of modules */
|
|
|
dcf580 |
list_init(&vmlinux_image.list);
|
|
|
dcf580 |
|
|
|
dcf580 |
+ list_init(&vmlinux_image_anon.list);
|
|
|
dcf580 |
+
|
|
|
dcf580 |
/* for no vmlinux */
|
|
|
dcf580 |
if (no_vmlinux) {
|
|
|
dcf580 |
vmlinux_image.name = "no-vmlinux";
|
|
|
dcf580 |
@@ -57,13 +68,22 @@ void opd_create_vmlinux(char const * nam
|
|
|
dcf580 |
vmlinux_image.start, vmlinux_image.end);
|
|
|
dcf580 |
exit(EXIT_FAILURE);
|
|
|
dcf580 |
}
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ vmlinux_image_anon.name = "vmlinux-unknown";
|
|
|
dcf580 |
+ vmlinux_image_anon.start = vmlinux_image.start;
|
|
|
dcf580 |
+ vmlinux_image_anon.end = vmlinux_image.end;
|
|
|
dcf580 |
+
|
|
|
dcf580 |
}
|
|
|
dcf580 |
|
|
|
dcf580 |
void opd_create_xen(char const * name, char const * arg)
|
|
|
dcf580 |
{
|
|
|
dcf580 |
+ int stat;
|
|
|
dcf580 |
+
|
|
|
dcf580 |
/* xen is *not* on the list of modules */
|
|
|
dcf580 |
list_init(&xen_image.list);
|
|
|
dcf580 |
|
|
|
dcf580 |
+ list_init(&xen_image_anon.list);
|
|
|
dcf580 |
+
|
|
|
dcf580 |
/* for no xen */
|
|
|
dcf580 |
if (no_xen) {
|
|
|
dcf580 |
xen_image.name = "no-xen";
|
|
|
dcf580 |
@@ -72,18 +92,106 @@ void opd_create_xen(char const * name, c
|
|
|
dcf580 |
|
|
|
dcf580 |
xen_image.name = xstrdup(name);
|
|
|
dcf580 |
|
|
|
dcf580 |
- sscanf(arg, "%llx,%llx", &xen_image.start, &xen_image.end);
|
|
|
dcf580 |
+ stat = sscanf(arg, "%llx,%llx", &xen_image.start, &xen_image.end);
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ xen_image_anon.name = "xen-unknown";
|
|
|
dcf580 |
+ xen_image_anon.start = xen_image.start;
|
|
|
dcf580 |
+ xen_image_anon.end = xen_image.end;
|
|
|
dcf580 |
|
|
|
dcf580 |
verbprintf(vmisc, "xen_start = %llx, xen_end = %llx\n",
|
|
|
dcf580 |
xen_image.start, xen_image.end);
|
|
|
dcf580 |
|
|
|
dcf580 |
- if (!xen_image.start && !xen_image.end) {
|
|
|
dcf580 |
+ if ( stat != 2 ) {
|
|
|
dcf580 |
fprintf(stderr, "error: mis-parsed xen range: %llx-%llx\n",
|
|
|
dcf580 |
xen_image.start, xen_image.end);
|
|
|
dcf580 |
exit(EXIT_FAILURE);
|
|
|
dcf580 |
}
|
|
|
dcf580 |
+
|
|
|
dcf580 |
}
|
|
|
dcf580 |
|
|
|
dcf580 |
+void opd_create_passive_domain(int id, char const * image_kernel,
|
|
|
dcf580 |
+ char const * range, char const * image_xen)
|
|
|
dcf580 |
+{
|
|
|
dcf580 |
+ char file[64];
|
|
|
dcf580 |
+ struct kernel_image * image;
|
|
|
dcf580 |
+ int stat;
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ image = xmalloc(sizeof(struct kernel_image));
|
|
|
dcf580 |
+ image->name = xstrdup(image_kernel);
|
|
|
dcf580 |
+ image->start = image->end = 0;
|
|
|
dcf580 |
+ stat = sscanf(range, "%llx,%llx", &image->start, &image->end);
|
|
|
dcf580 |
+ image->id = id;
|
|
|
dcf580 |
+ list_add(&image->list, &passive_vmlinux);
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ if ( stat != 2 ) {
|
|
|
dcf580 |
+ fprintf(stderr, "error: mis-parsed passive domain range for "
|
|
|
dcf580 |
+ "domain %d: %llx-%llx\n", id, image->start, image->end);
|
|
|
dcf580 |
+ exit(EXIT_FAILURE);
|
|
|
dcf580 |
+ }
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ image = xmalloc(sizeof(struct kernel_image));
|
|
|
dcf580 |
+ image->name = xstrdup(image_xen);
|
|
|
dcf580 |
+ image->start = xen_image.start;
|
|
|
dcf580 |
+ image->end = xen_image.end;
|
|
|
dcf580 |
+ image->id = id;
|
|
|
dcf580 |
+ list_add(&image->list, &passive_xen);
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ sprintf(file, "domain%d-apps", id);
|
|
|
dcf580 |
+ image = xmalloc(sizeof(struct kernel_image));
|
|
|
dcf580 |
+ image->name = xstrdup(file);
|
|
|
dcf580 |
+ image->start = 0;
|
|
|
dcf580 |
+ image->end = 0;
|
|
|
dcf580 |
+ image->id = id;
|
|
|
dcf580 |
+ list_add(&image->list, &passive_apps);
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ sprintf(file, "domain%d-modules", id);
|
|
|
dcf580 |
+ image = xmalloc(sizeof(struct kernel_image));
|
|
|
dcf580 |
+ image->name = xstrdup(file);
|
|
|
dcf580 |
+ image->start = 0;
|
|
|
dcf580 |
+ image->end = 0;
|
|
|
dcf580 |
+ stat = sscanf(range, "%llx,%llx", &image->start, &image->end);
|
|
|
dcf580 |
+ image->id = id;
|
|
|
dcf580 |
+ list_add(&image->list, &passive_modules);
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ sprintf(file, "domain%d-xen-unknown", id);
|
|
|
dcf580 |
+ image = xmalloc(sizeof(struct kernel_image));
|
|
|
dcf580 |
+ image->name = xstrdup(file);
|
|
|
dcf580 |
+ image->start = xen_image.start;
|
|
|
dcf580 |
+ image->end = xen_image.end;
|
|
|
dcf580 |
+ image->id = id;
|
|
|
dcf580 |
+ list_add(&image->list, &passive_xen_anon);
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+}
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+void opd_create_passive(char const *setup_file)
|
|
|
dcf580 |
+{
|
|
|
dcf580 |
+ FILE *fp;
|
|
|
dcf580 |
+ int id=0;
|
|
|
dcf580 |
+ char image_kernel[128+1];
|
|
|
dcf580 |
+ char range[128+1];
|
|
|
dcf580 |
+ char image_xen[128+1];
|
|
|
dcf580 |
+ int stat;
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ image_kernel[0] = range[0] = image_xen[0] = 0;
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ fp = fopen(setup_file, "r");
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ if (!fp) {
|
|
|
dcf580 |
+ fprintf(stderr, "error: Could not open Xen passive domain "
|
|
|
dcf580 |
+ "setup file %s\n", setup_file);
|
|
|
dcf580 |
+ exit(EXIT_FAILURE);
|
|
|
dcf580 |
+ }
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ while (1) {
|
|
|
dcf580 |
+ stat = fscanf(fp, "%d %128s %128s %128s", &id, image_kernel, range,
|
|
|
dcf580 |
+ image_xen);
|
|
|
dcf580 |
+ if ( stat != 4 )
|
|
|
dcf580 |
+ return;
|
|
|
dcf580 |
+ opd_create_passive_domain(id, image_kernel, range, image_xen);
|
|
|
dcf580 |
+ }
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ fclose(fp);
|
|
|
dcf580 |
+}
|
|
|
dcf580 |
|
|
|
dcf580 |
/**
|
|
|
dcf580 |
* Allocate and initialise a kernel image description
|
|
|
dcf580 |
@@ -210,6 +318,75 @@ struct kernel_image * find_kernel_image(
|
|
|
dcf580 |
struct list_head * pos;
|
|
|
dcf580 |
struct kernel_image * image = &vmlinux_image;
|
|
|
dcf580 |
|
|
|
dcf580 |
+ if (current_domain != COORDINATOR_DOMAIN) {
|
|
|
dcf580 |
+ /* we rely on cpu_mode value (i.e. trans->in_kernel)
|
|
|
dcf580 |
+ * to search the right image type: xen, kernel or user
|
|
|
dcf580 |
+ * We cannot use address ranges since hypervisor does not
|
|
|
dcf580 |
+ * share the same address space with fully virtualized guests,
|
|
|
dcf580 |
+ * and thus address ranges can overlap */
|
|
|
dcf580 |
+ switch ( trans->in_kernel ) {
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ /* user mode */
|
|
|
dcf580 |
+ case 1:
|
|
|
dcf580 |
+ list_for_each(pos, &passive_apps) {
|
|
|
dcf580 |
+ image = list_entry(pos, struct kernel_image, list);
|
|
|
dcf580 |
+ if (image->id == current_domain)
|
|
|
dcf580 |
+ return image;
|
|
|
dcf580 |
+ }
|
|
|
dcf580 |
+ return NULL;
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ /* kernel mode */
|
|
|
dcf580 |
+ case 2:
|
|
|
dcf580 |
+ list_for_each(pos, &passive_vmlinux) {
|
|
|
dcf580 |
+ image = list_entry(pos, struct kernel_image, list);
|
|
|
dcf580 |
+ if ( (image->id == current_domain)
|
|
|
dcf580 |
+ && ( (image->start == 0 && image->end == 0)
|
|
|
dcf580 |
+ || (image->start <= trans->pc
|
|
|
dcf580 |
+ && image->end > trans->pc) ) )
|
|
|
dcf580 |
+ return image;
|
|
|
dcf580 |
+ }
|
|
|
dcf580 |
+ /* if not in kernel image range then it should be a module */
|
|
|
dcf580 |
+ list_for_each(pos, &passive_modules) {
|
|
|
dcf580 |
+ image = list_entry(pos, struct kernel_image, list);
|
|
|
dcf580 |
+ if (image->id == current_domain)
|
|
|
dcf580 |
+ return image;
|
|
|
dcf580 |
+ }
|
|
|
dcf580 |
+ /* This should not happen if the kernel and user level
|
|
|
dcf580 |
+ oprofile code are sane and in sync */
|
|
|
dcf580 |
+ return NULL;
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ /* hypervisor mode */
|
|
|
dcf580 |
+ case 3:
|
|
|
dcf580 |
+ list_for_each(pos, &passive_xen) {
|
|
|
dcf580 |
+ image = list_entry(pos, struct kernel_image, list);
|
|
|
dcf580 |
+ if (image->id == current_domain
|
|
|
dcf580 |
+ && image->start <= trans->pc
|
|
|
dcf580 |
+ && image->end > trans->pc)
|
|
|
dcf580 |
+ return image;
|
|
|
dcf580 |
+ }
|
|
|
dcf580 |
+ list_for_each(pos, &passive_xen_anon) {
|
|
|
dcf580 |
+ image = list_entry(pos, struct kernel_image, list);
|
|
|
dcf580 |
+ if (image->id == current_domain)
|
|
|
dcf580 |
+ return image;
|
|
|
dcf580 |
+ }
|
|
|
dcf580 |
+ return NULL;
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ default:
|
|
|
dcf580 |
+ printf("Unexpected error on passive mode: CPU mode is "
|
|
|
dcf580 |
+ "%d for domain %d\n", trans->in_kernel, current_domain);
|
|
|
dcf580 |
+ return NULL;
|
|
|
dcf580 |
+ }
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ }
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ if (xen_image.start <= trans->pc && xen_image.end > trans->pc)
|
|
|
dcf580 |
+ return &xen_image;
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ if (trans->in_kernel == 2) {
|
|
|
dcf580 |
+ return &xen_image_anon;
|
|
|
dcf580 |
+ }
|
|
|
dcf580 |
+
|
|
|
dcf580 |
if (no_vmlinux)
|
|
|
dcf580 |
return image;
|
|
|
dcf580 |
|
|
|
dcf580 |
@@ -222,8 +399,5 @@ struct kernel_image * find_kernel_image(
|
|
|
dcf580 |
return image;
|
|
|
dcf580 |
}
|
|
|
dcf580 |
|
|
|
dcf580 |
- if (xen_image.start <= trans->pc && xen_image.end > trans->pc)
|
|
|
dcf580 |
- return &xen_image;
|
|
|
dcf580 |
-
|
|
|
dcf580 |
- return NULL;
|
|
|
dcf580 |
+ return &vmlinux_image_anon;
|
|
|
dcf580 |
}
|
|
|
dcf580 |
diff -up oprofile-0.9.7/daemon/opd_kernel.h.xen oprofile-0.9.7/daemon/opd_kernel.h
|
|
|
dcf580 |
--- oprofile-0.9.7/daemon/opd_kernel.h.xen 2011-07-04 22:25:04.000000000 -0400
|
|
|
dcf580 |
+++ oprofile-0.9.7/daemon/opd_kernel.h 2011-11-28 16:25:07.580000010 -0500
|
|
|
dcf580 |
@@ -23,8 +23,12 @@ struct transient;
|
|
|
dcf580 |
/** create the kernel image */
|
|
|
dcf580 |
void opd_create_vmlinux(char const * name, char const * arg);
|
|
|
dcf580 |
|
|
|
dcf580 |
+/** create Xen image */
|
|
|
dcf580 |
void opd_create_xen(char const * name, char const * arg);
|
|
|
dcf580 |
|
|
|
dcf580 |
+/** create Xen passive domain images */
|
|
|
dcf580 |
+void opd_create_passive(char const *setup_file);
|
|
|
dcf580 |
+
|
|
|
dcf580 |
/** opd_reread_module_info - parse /proc/modules for kernel modules */
|
|
|
dcf580 |
void opd_reread_module_info(void);
|
|
|
dcf580 |
|
|
|
dcf580 |
@@ -33,6 +37,7 @@ struct kernel_image {
|
|
|
dcf580 |
char * name;
|
|
|
dcf580 |
vma_t start;
|
|
|
dcf580 |
vma_t end;
|
|
|
dcf580 |
+ int id;
|
|
|
dcf580 |
struct list_head list;
|
|
|
dcf580 |
};
|
|
|
dcf580 |
|
|
|
dcf580 |
diff -up oprofile-0.9.7/daemon/opd_sfile.c.xen oprofile-0.9.7/daemon/opd_sfile.c
|
|
|
dcf580 |
--- oprofile-0.9.7/daemon/opd_sfile.c.xen 2011-07-04 22:25:04.000000000 -0400
|
|
|
dcf580 |
+++ oprofile-0.9.7/daemon/opd_sfile.c 2011-11-28 16:25:07.582000010 -0500
|
|
|
dcf580 |
@@ -240,7 +240,7 @@ struct sfile * sfile_find(struct transie
|
|
|
dcf580 |
}
|
|
|
dcf580 |
|
|
|
dcf580 |
/* we might need a kernel image start/end to hash on */
|
|
|
dcf580 |
- if (trans->in_kernel) {
|
|
|
dcf580 |
+ else if (trans->in_kernel) {
|
|
|
dcf580 |
ki = find_kernel_image(trans);
|
|
|
dcf580 |
if (!ki) {
|
|
|
dcf580 |
verbprintf(vsamples, "Lost kernel sample %llx\n", trans->pc);
|
|
|
dcf580 |
diff -up oprofile-0.9.7/daemon/opd_trans.c.xen oprofile-0.9.7/daemon/opd_trans.c
|
|
|
dcf580 |
--- oprofile-0.9.7/daemon/opd_trans.c.xen 2011-07-04 22:25:04.000000000 -0400
|
|
|
dcf580 |
+++ oprofile-0.9.7/daemon/opd_trans.c 2011-11-28 16:25:07.584000010 -0500
|
|
|
dcf580 |
@@ -31,6 +31,8 @@
|
|
|
dcf580 |
#include <stdio.h>
|
|
|
dcf580 |
#include <errno.h>
|
|
|
dcf580 |
|
|
|
dcf580 |
+int32_t current_domain = COORDINATOR_DOMAIN;
|
|
|
dcf580 |
+
|
|
|
dcf580 |
extern size_t kernel_pointer_size;
|
|
|
dcf580 |
|
|
|
dcf580 |
|
|
|
dcf580 |
@@ -203,6 +205,9 @@ static void code_kernel_enter(struct tra
|
|
|
dcf580 |
{
|
|
|
dcf580 |
verbprintf(vmisc, "KERNEL_ENTER_SWITCH to kernel\n");
|
|
|
dcf580 |
trans->in_kernel = 1;
|
|
|
dcf580 |
+ /* if in passive domain mode cpu mode should be incremented */
|
|
|
dcf580 |
+ if (current_domain != COORDINATOR_DOMAIN)
|
|
|
dcf580 |
+ trans->in_kernel++;
|
|
|
dcf580 |
clear_trans_current(trans);
|
|
|
dcf580 |
/* subtlety: we must keep trans->cookie cached,
|
|
|
dcf580 |
* even though it's meaningless for the kernel -
|
|
|
dcf580 |
@@ -216,6 +221,9 @@ static void code_user_enter(struct trans
|
|
|
dcf580 |
{
|
|
|
dcf580 |
verbprintf(vmisc, "USER_ENTER_SWITCH to user-space\n");
|
|
|
dcf580 |
trans->in_kernel = 0;
|
|
|
dcf580 |
+ /* if in passive domain mode cpu mode should be incremented */
|
|
|
dcf580 |
+ if (current_domain != COORDINATOR_DOMAIN)
|
|
|
dcf580 |
+ trans->in_kernel++;
|
|
|
dcf580 |
clear_trans_current(trans);
|
|
|
dcf580 |
clear_trans_last(trans);
|
|
|
dcf580 |
}
|
|
|
dcf580 |
@@ -244,17 +252,34 @@ static void code_trace_begin(struct tran
|
|
|
dcf580 |
static void code_xen_enter(struct transient * trans)
|
|
|
dcf580 |
{
|
|
|
dcf580 |
verbprintf(vmisc, "XEN_ENTER_SWITCH to xen\n");
|
|
|
dcf580 |
- trans->in_kernel = 1;
|
|
|
dcf580 |
+ trans->in_kernel = 2;
|
|
|
dcf580 |
+ /* if in passive domain mode cpu mode should be incremented */
|
|
|
dcf580 |
+ if (current_domain != COORDINATOR_DOMAIN)
|
|
|
dcf580 |
+ trans->in_kernel++;
|
|
|
dcf580 |
trans->current = NULL;
|
|
|
dcf580 |
/* subtlety: we must keep trans->cookie cached, even though it's
|
|
|
dcf580 |
- * meaningless for Xen - we won't necessarily get a cookie switch
|
|
|
dcf580 |
- * on Xen exit. See comments in opd_sfile.c. It seems that we can
|
|
|
dcf580 |
- * get away with in_kernel = 1 as long as we supply the correct
|
|
|
dcf580 |
- * Xen image, and its address range in startup find_kernel_image
|
|
|
dcf580 |
- * is modified to look in the Xen image also
|
|
|
dcf580 |
- */
|
|
|
dcf580 |
+ * meaningless for Xen - same reason as for kernel */
|
|
|
dcf580 |
}
|
|
|
dcf580 |
|
|
|
dcf580 |
+static void code_domain_switch(struct transient *trans)
|
|
|
dcf580 |
+{
|
|
|
dcf580 |
+ /* While processing passive domain samples we ensure (in_kernel!=0)
|
|
|
dcf580 |
+ * We do this in order to ignore cookies for passive domain samples
|
|
|
dcf580 |
+ * But, we have to remember the kernel value for coordinator domain,
|
|
|
dcf580 |
+ * so we do the safe thing: increment when leaving the coordinator
|
|
|
dcf580 |
+ * domain and decrement when returning to it
|
|
|
dcf580 |
+ */
|
|
|
dcf580 |
+ if (current_domain == COORDINATOR_DOMAIN)
|
|
|
dcf580 |
+ trans->in_kernel++;
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ trans->current = NULL;
|
|
|
dcf580 |
+ current_domain = (int32_t) pop_buffer_value(trans);
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ /* If returning to coordinator domain restore the kernel value */
|
|
|
dcf580 |
+ if (current_domain == COORDINATOR_DOMAIN)
|
|
|
dcf580 |
+ trans->in_kernel--;
|
|
|
dcf580 |
+}
|
|
|
dcf580 |
+
|
|
|
dcf580 |
extern void code_spu_profiling(struct transient * trans);
|
|
|
dcf580 |
extern void code_spu_ctx_switch(struct transient * trans);
|
|
|
dcf580 |
|
|
|
dcf580 |
@@ -278,7 +303,7 @@ handler_t handlers[LAST_CODE + 1] = {
|
|
|
dcf580 |
&code_spu_profiling,
|
|
|
dcf580 |
&code_spu_ctx_switch,
|
|
|
dcf580 |
#else
|
|
|
dcf580 |
- &code_unknown,
|
|
|
dcf580 |
+ &code_domain_switch,
|
|
|
dcf580 |
&code_unknown,
|
|
|
dcf580 |
#endif
|
|
|
dcf580 |
&code_ibs_fetch_sample,
|
|
|
dcf580 |
diff -up oprofile-0.9.7/daemon/opd_trans.h.xen oprofile-0.9.7/daemon/opd_trans.h
|
|
|
dcf580 |
--- oprofile-0.9.7/daemon/opd_trans.h.xen 2011-07-04 22:25:04.000000000 -0400
|
|
|
dcf580 |
+++ oprofile-0.9.7/daemon/opd_trans.h 2011-11-28 16:25:07.585000010 -0500
|
|
|
dcf580 |
@@ -21,6 +21,10 @@
|
|
|
dcf580 |
|
|
|
dcf580 |
#include <stdint.h>
|
|
|
dcf580 |
|
|
|
dcf580 |
+#define COORDINATOR_DOMAIN -1
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+extern int32_t current_domain;
|
|
|
dcf580 |
+
|
|
|
dcf580 |
struct sfile;
|
|
|
dcf580 |
struct anon_mapping;
|
|
|
dcf580 |
|
|
|
dcf580 |
diff -up oprofile-0.9.7/daemon/oprofiled.c.xen oprofile-0.9.7/daemon/oprofiled.c
|
|
|
dcf580 |
--- oprofile-0.9.7/daemon/oprofiled.c.xen 2011-07-04 22:25:04.000000000 -0400
|
|
|
dcf580 |
+++ oprofile-0.9.7/daemon/oprofiled.c 2011-11-28 16:25:07.587000010 -0500
|
|
|
dcf580 |
@@ -71,6 +71,7 @@ char * session_dir;
|
|
|
dcf580 |
int no_xen;
|
|
|
dcf580 |
char * xenimage;
|
|
|
dcf580 |
char * xen_range;
|
|
|
dcf580 |
+char * xen_passive_setup;
|
|
|
dcf580 |
static char * verbose;
|
|
|
dcf580 |
static char * binary_name_filter;
|
|
|
dcf580 |
static char * events;
|
|
|
dcf580 |
@@ -91,6 +92,7 @@ static struct poptOption options[] = {
|
|
|
dcf580 |
{ "xen-range", 0, POPT_ARG_STRING, &xen_range, 0, "Xen VMA range", "start-end", },
|
|
|
dcf580 |
{ "xen-image", 0, POPT_ARG_STRING, &xenimage, 0, "Xen image", "file", },
|
|
|
dcf580 |
{ "image", 0, POPT_ARG_STRING, &binary_name_filter, 0, "image name filter", "profile these comma separated image" },
|
|
|
dcf580 |
+ { "xen-passive-setup", 0, POPT_ARG_STRING, &xen_passive_setup, 0, "Xen passive domain setup file", "filename", },
|
|
|
dcf580 |
{ "separate-lib", 0, POPT_ARG_INT, &separate_lib, 0, "separate library samples for each distinct application", "[0|1]", },
|
|
|
dcf580 |
{ "separate-kernel", 0, POPT_ARG_INT, &separate_kernel, 0, "separate kernel samples for each distinct application", "[0|1]", },
|
|
|
dcf580 |
{ "separate-thread", 0, POPT_ARG_INT, &separate_thread, 0, "thread-profiling mode", "[0|1]" },
|
|
|
dcf580 |
diff -up oprofile-0.9.7/daemon/oprofiled.h.xen oprofile-0.9.7/daemon/oprofiled.h
|
|
|
dcf580 |
--- oprofile-0.9.7/daemon/oprofiled.h.xen 2011-07-04 22:25:04.000000000 -0400
|
|
|
dcf580 |
+++ oprofile-0.9.7/daemon/oprofiled.h 2011-11-28 16:25:07.588000010 -0500
|
|
|
dcf580 |
@@ -65,5 +65,6 @@ extern char * kernel_range;
|
|
|
dcf580 |
extern int no_xen;
|
|
|
dcf580 |
extern char * xenimage;
|
|
|
dcf580 |
extern char * xen_range;
|
|
|
dcf580 |
+extern char * xen_passive_setup;
|
|
|
dcf580 |
|
|
|
dcf580 |
#endif /* OPROFILED_H */
|
|
|
dcf580 |
diff -up oprofile-0.9.7/doc/opcontrol.1.in.xen oprofile-0.9.7/doc/opcontrol.1.in
|
|
|
dcf580 |
--- oprofile-0.9.7/doc/opcontrol.1.in.xen 2011-07-04 22:25:04.000000000 -0400
|
|
|
dcf580 |
+++ oprofile-0.9.7/doc/opcontrol.1.in 2011-11-28 16:25:07.590000010 -0500
|
|
|
dcf580 |
@@ -158,12 +158,41 @@ Xen image
|
|
|
dcf580 |
.br
|
|
|
dcf580 |
.TP
|
|
|
dcf580 |
.BI "--active-domains="<list>
|
|
|
dcf580 |
-List of domain ids participating in a multi-domain profiling session. If
|
|
|
dcf580 |
+List of domain ids participating in a multi-domain profiling session.
|
|
|
dcf580 |
+Each of the specified domains must run an instance of oprofile. The
|
|
|
dcf580 |
+sequence of opcontrol commands in each domain must follow a given
|
|
|
dcf580 |
+order which is specified in the oprofile user manual. If
|
|
|
dcf580 |
more than one domain is specified in <list> they should be separated using
|
|
|
dcf580 |
commas. This option can only be used in domain 0 which is the only domain
|
|
|
dcf580 |
that can coordinate a multi-domain profiling session. Including domain 0 in
|
|
|
dcf580 |
the list of active domains is optional. (e.g. --active-domains=2,5,6 and
|
|
|
dcf580 |
---active-domains=0,2,5,6 are equivalent)
|
|
|
dcf580 |
+--active-domains=0,2,5,6 are equivalent).
|
|
|
dcf580 |
+This option can only be specified
|
|
|
dcf580 |
+if --start-daemon is also specified and it is only
|
|
|
dcf580 |
+valid for the current run of the oprofile daemon; e.g. the list
|
|
|
dcf580 |
+of active domains is not persistent.
|
|
|
dcf580 |
+.br
|
|
|
dcf580 |
+.TP
|
|
|
dcf580 |
+.BI "--passive-domains="<list> or "--domains="<list>
|
|
|
dcf580 |
+List of domain ids to be profiled, separated by commas.
|
|
|
dcf580 |
+As opposed to the --active-domains option, the domains specified with this
|
|
|
dcf580 |
+option do not need to run oprofile. This makes
|
|
|
dcf580 |
+profiling multiple domains easier. However, with the passive-domains option,
|
|
|
dcf580 |
+samples in user level processes and kernel modules cannot be
|
|
|
dcf580 |
+mapped to specific symbols and are aggregated
|
|
|
dcf580 |
+under a generic class. Both --active-domains and --passive-domains
|
|
|
dcf580 |
+options can be specified in the same command, but the same domain cannot be
|
|
|
dcf580 |
+specified in both options. This option can only be specified if either --start
|
|
|
dcf580 |
+or --start-daemon is specified on the same command and it is only valid for
|
|
|
dcf580 |
+the current run of the oprofile daemon; e.g. the list of passive domains is
|
|
|
dcf580 |
+not persistent.
|
|
|
dcf580 |
+.br
|
|
|
dcf580 |
+.TP
|
|
|
dcf580 |
+.BI "--passive-images="<list> or "--domains-images="<list>
|
|
|
dcf580 |
+List of kernel images associated with the domains specified in the
|
|
|
dcf580 |
+--passive-domains option, also separated by commas. The association
|
|
|
dcf580 |
+between the images and domains is based on the order they are
|
|
|
dcf580 |
+specified in both options.
|
|
|
dcf580 |
.br
|
|
|
dcf580 |
.SH OPTIONS (specific to System z)
|
|
|
dcf580 |
.TP
|
|
|
dcf580 |
diff -up oprofile-0.9.7/libpp/format_output.cpp.xen oprofile-0.9.7/libpp/format_output.cpp
|
|
|
dcf580 |
--- oprofile-0.9.7/libpp/format_output.cpp.xen 2011-07-04 22:25:04.000000000 -0400
|
|
|
dcf580 |
+++ oprofile-0.9.7/libpp/format_output.cpp 2011-11-28 16:25:07.592000010 -0500
|
|
|
dcf580 |
@@ -287,8 +287,8 @@ string formatter::format_app_name(field_
|
|
|
dcf580 |
{
|
|
|
dcf580 |
return get_image_name(f.symbol.app_name,
|
|
|
dcf580 |
long_filenames
|
|
|
dcf580 |
- ? image_name_storage::int_real_filename
|
|
|
dcf580 |
- : image_name_storage::int_real_basename,
|
|
|
dcf580 |
+ ? image_name_storage::int_filename
|
|
|
dcf580 |
+ : image_name_storage::int_basename,
|
|
|
dcf580 |
extra_found_images);
|
|
|
dcf580 |
}
|
|
|
dcf580 |
|
|
|
dcf580 |
diff -up oprofile-0.9.7/utils/opcontrol.xen oprofile-0.9.7/utils/opcontrol
|
|
|
dcf580 |
--- oprofile-0.9.7/utils/opcontrol.xen 2011-07-20 15:36:48.000000000 -0400
|
|
|
dcf580 |
+++ oprofile-0.9.7/utils/opcontrol 2011-11-28 16:28:56.431000248 -0500
|
|
|
dcf580 |
@@ -236,9 +236,16 @@ opcontrol: usage:
|
|
|
dcf580 |
buffer-size.
|
|
|
dcf580 |
--cpu-buffer-size=num per-cpu buffer size in units (2.6 kernel)
|
|
|
dcf580 |
Same rules as defined for buffer-size.
|
|
|
dcf580 |
- --xen Xen image (for Xen only)
|
|
|
dcf580 |
- --active-domains=<list> List of domains in profiling session (for Xen)
|
|
|
dcf580 |
- (list contains domain ids separated by commas)
|
|
|
dcf580 |
+ --xen=file Xen image (for Xen only)
|
|
|
dcf580 |
+ --active-domains=id[,ids] list of domains in multiple domain profiling session (Xen)
|
|
|
dcf580 |
+ (detailed profiling of user level and kernel modules code)
|
|
|
dcf580 |
+ (requires running oprofile on these domains)
|
|
|
dcf580 |
+ --passive-domains=id[,ids] list of domains to be profiled (Xen).
|
|
|
dcf580 |
+ or --domains=id[,ids] (coarse profiling of user level and kernel modules code)
|
|
|
dcf580 |
+ (no need to run oprofile on these domains)
|
|
|
dcf580 |
+ --passive-images=file[,files] list of kernel images associated with each passive domain
|
|
|
dcf580 |
+ or
|
|
|
dcf580 |
+ --domain-images=file[,files]
|
|
|
dcf580 |
|
|
|
dcf580 |
System z specific options
|
|
|
dcf580 |
|
|
|
dcf580 |
@@ -388,6 +395,9 @@ do_init()
|
|
|
dcf580 |
SETUP_FILE="$SETUP_DIR/daemonrc"
|
|
|
dcf580 |
SEC_SETUP_FILE="$SETUP_DIR/daemonrc_new"
|
|
|
dcf580 |
|
|
|
dcf580 |
+ # location for passing info about passive domains to daemon
|
|
|
dcf580 |
+ PASSIVE_SETUP_FILE="$SETUP_DIR/xendomain.setup"
|
|
|
dcf580 |
+
|
|
|
dcf580 |
# initialize daemon vars
|
|
|
dcf580 |
decide_oprofile_device_mount
|
|
|
dcf580 |
CPUTYPE=`cat $MOUNT/cpu_type`
|
|
|
dcf580 |
@@ -539,7 +549,7 @@ do_load_setup()
|
|
|
dcf580 |
}
|
|
|
dcf580 |
|
|
|
dcf580 |
|
|
|
dcf580 |
-check_valid_args()
|
|
|
dcf580 |
+check_valid_vmlinux()
|
|
|
dcf580 |
{
|
|
|
dcf580 |
if test -z "$VMLINUX"; then
|
|
|
dcf580 |
echo "No vmlinux file specified. You must specify the correct vmlinux file, e.g." >&2
|
|
|
dcf580 |
@@ -560,8 +570,12 @@ check_valid_args()
|
|
|
dcf580 |
|
|
|
dcf580 |
echo "The specified vmlinux file \"$VMLINUX\" doesn't exist." >&2
|
|
|
dcf580 |
exit 1
|
|
|
dcf580 |
+}
|
|
|
dcf580 |
+
|
|
|
dcf580 |
|
|
|
dcf580 |
# similar check for Xen image
|
|
|
dcf580 |
+check_valid_xen()
|
|
|
dcf580 |
+{
|
|
|
dcf580 |
if test -f "$XENIMAGE"; then
|
|
|
dcf580 |
return
|
|
|
dcf580 |
fi
|
|
|
dcf580 |
@@ -622,6 +636,77 @@ get_image_range()
|
|
|
dcf580 |
}
|
|
|
dcf580 |
|
|
|
dcf580 |
|
|
|
dcf580 |
+set_passive_domain()
|
|
|
dcf580 |
+{
|
|
|
dcf580 |
+ DOMAIN_ID=$1
|
|
|
dcf580 |
+ FILE_IMAGE=$2
|
|
|
dcf580 |
+ XEN_IMAGE=$3
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ if test "$FILE_IMAGE" = "none"; then
|
|
|
dcf580 |
+ RANGE="0,0"
|
|
|
dcf580 |
+ FILE_IMAGE="domain$DOMAIN_ID-kernel"
|
|
|
dcf580 |
+ else
|
|
|
dcf580 |
+ # Find VMA range for passive domain kernel image
|
|
|
dcf580 |
+ range_info=`objdump -h $FILE_IMAGE 2>/dev/null | grep " .text "`
|
|
|
dcf580 |
+ tmp1=`echo $range_info | awk '{print $4}'`
|
|
|
dcf580 |
+ tmp_length=`echo $range_info | awk '{print $3}'`
|
|
|
dcf580 |
+ tmp2=`objdump -h $FILE_IMAGE --adjust-vma=0x$tmp_length 2>/dev/null | grep " .text " | awk '{print $4}'`
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ if test -z "$tmp1" -o -z "$tmp2"; then
|
|
|
dcf580 |
+ echo "The specified file $FILE_IMAGE does not seem to be valid" >&2
|
|
|
dcf580 |
+ echo "Make sure you are using the non-compressed image file (e.g. vmlinux not vmlinuz)" >&2
|
|
|
dcf580 |
+ vecho "found start as \"$tmp1\", end as \"$tmp2\"" >&2
|
|
|
dcf580 |
+ exit 1
|
|
|
dcf580 |
+ fi
|
|
|
dcf580 |
+ RANGE="`echo $tmp1`,`echo $tmp2`"
|
|
|
dcf580 |
+ fi
|
|
|
dcf580 |
+ echo " $DOMAIN_ID $FILE_IMAGE $RANGE $XEN_IMAGE" >> $PASSIVE_SETUP_FILE
|
|
|
dcf580 |
+}
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+set_passive_domain_config()
|
|
|
dcf580 |
+{
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ create_dir "$SETUP_DIR"
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ touch $PASSIVE_SETUP_FILE
|
|
|
dcf580 |
+ chmod 644 $PASSIVE_SETUP_FILE
|
|
|
dcf580 |
+ >$PASSIVE_SETUP_FILE
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ NDOMAINS=`echo "$PASSIVE_DOMAINS" | awk -F',' '{print NF}'`
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ if test -n "$PASSIVE_IMAGES"; then
|
|
|
dcf580 |
+ NIMAGES=`echo "$PASSIVE_IMAGES" | awk -F',' '{print NF}'`
|
|
|
dcf580 |
+ if [ $NDOMAINS != $NIMAGES ]; then
|
|
|
dcf580 |
+ echo "# of passive domains and # of passive images doesn't match." >&2
|
|
|
dcf580 |
+ do_help
|
|
|
dcf580 |
+ exit 1
|
|
|
dcf580 |
+ fi
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ for (( i=1; i<=$NDOMAINS; i++ )); do
|
|
|
dcf580 |
+ ID=`echo "$PASSIVE_DOMAINS" | awk -F"," '{print $'$i'}'`
|
|
|
dcf580 |
+ FILE=`echo "$PASSIVE_IMAGES" | awk -F',' '{print $'$i'}'`
|
|
|
dcf580 |
+ if test ! -f "$FILE"; then
|
|
|
dcf580 |
+ echo "Image $FILE for passive domain $ID not found." >&2
|
|
|
dcf580 |
+ return 1
|
|
|
dcf580 |
+ fi
|
|
|
dcf580 |
+ LNK_KERNEL=/boot/domain$ID-kernel
|
|
|
dcf580 |
+ ln -sf $FILE $LNK_KERNEL
|
|
|
dcf580 |
+ LNK_XEN=/boot/domain$ID-xen
|
|
|
dcf580 |
+ ln -sf $XENIMAGE $LNK_XEN
|
|
|
dcf580 |
+ set_passive_domain $ID $LNK_KERNEL $LNK_XEN
|
|
|
dcf580 |
+ done
|
|
|
dcf580 |
+ else
|
|
|
dcf580 |
+ for (( i=1; i<=$NDOMAINS; i++ )); do
|
|
|
dcf580 |
+ ID=`echo "$PASSIVE_DOMAINS" | awk -F"," '{print $'$i'}'`
|
|
|
dcf580 |
+ LNK_XEN=/boot/domain$ID-xen
|
|
|
dcf580 |
+ set_passive_domain $ID none $LNK_XEN
|
|
|
dcf580 |
+ done
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ fi
|
|
|
dcf580 |
+}
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+
|
|
|
dcf580 |
# validate --separate= parameters. This function is called with IFS=,
|
|
|
dcf580 |
# so on each argument is splitted
|
|
|
dcf580 |
validate_separate_args()
|
|
|
dcf580 |
@@ -932,10 +1017,20 @@ do_options()
|
|
|
dcf580 |
DO_SETUP=yes
|
|
|
dcf580 |
;;
|
|
|
dcf580 |
--active-domains)
|
|
|
dcf580 |
- error_if_invalid_arg $arg $val
|
|
|
dcf580 |
+ error_if_invalid_arg "$arg" "$val"
|
|
|
dcf580 |
ACTIVE_DOMAINS=$val
|
|
|
dcf580 |
DO_SETUP=yes
|
|
|
dcf580 |
;;
|
|
|
dcf580 |
+ --passive-domains|--domains)
|
|
|
dcf580 |
+ error_if_invalid_arg "$arg" "$val"
|
|
|
dcf580 |
+ PASSIVE_DOMAINS=$val
|
|
|
dcf580 |
+ DO_SETUP=yes
|
|
|
dcf580 |
+ ;;
|
|
|
dcf580 |
+ --passive-images|--domain-images)
|
|
|
dcf580 |
+ error_if_invalid_arg "$arg" "$val"
|
|
|
dcf580 |
+ PASSIVE_IMAGES=$val
|
|
|
dcf580 |
+ DO_SETUP=yes
|
|
|
dcf580 |
+ ;;
|
|
|
dcf580 |
-i|--image)
|
|
|
dcf580 |
error_if_invalid_arg "$arg" "$val"
|
|
|
dcf580 |
if test "$val" = "all"; then
|
|
|
dcf580 |
@@ -1366,6 +1461,16 @@ check_event_mapping_data()
|
|
|
dcf580 |
exit 1
|
|
|
dcf580 |
fi
|
|
|
dcf580 |
fi
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ if test -n "$ACTIVE_DOMAINS" -a "$START_DAEMON" != "yes"; then
|
|
|
dcf580 |
+ echo "Option \"--active-domains\" can only be used with option \"-start-daemon\"." >&2
|
|
|
dcf580 |
+ exit 1
|
|
|
dcf580 |
+ fi
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ if test -n "$PASSIVE_DOMAINS" -a "$START_DAEMON" != "yes" -a "$START" != "yes"; then
|
|
|
dcf580 |
+ echo "Option \"--passive-domains\" or "--domains" can only be used with option \"--start-daemon\" or \"--start\"." >&2
|
|
|
dcf580 |
+ exit 1
|
|
|
dcf580 |
+ fi
|
|
|
dcf580 |
}
|
|
|
dcf580 |
|
|
|
dcf580 |
|
|
|
dcf580 |
@@ -1404,6 +1509,15 @@ do_param_setup()
|
|
|
dcf580 |
fi
|
|
|
dcf580 |
fi
|
|
|
dcf580 |
|
|
|
dcf580 |
+ if test -n "$PASSIVE_DOMAINS"; then
|
|
|
dcf580 |
+ if test "$KERNEL_SUPPORT" = "yes"; then
|
|
|
dcf580 |
+ echo $PASSIVE_DOMAINS >$MOUNT/passive_domains
|
|
|
dcf580 |
+ set_passive_domain_config
|
|
|
dcf580 |
+ else
|
|
|
dcf580 |
+ echo "passive-domains not supported - ignored" >&2
|
|
|
dcf580 |
+ fi
|
|
|
dcf580 |
+ fi
|
|
|
dcf580 |
+
|
|
|
dcf580 |
if test $NOTE_SIZE != 0; then
|
|
|
dcf580 |
set_param notesize $NOTE_SIZE
|
|
|
dcf580 |
fi
|
|
|
dcf580 |
@@ -1566,7 +1680,8 @@ do_start_daemon()
|
|
|
dcf580 |
fi
|
|
|
dcf580 |
|
|
|
dcf580 |
do_setup
|
|
|
dcf580 |
- check_valid_args
|
|
|
dcf580 |
+ check_valid_vmlinux
|
|
|
dcf580 |
+ check_valid_xen
|
|
|
dcf580 |
get_image_range "linux"
|
|
|
dcf580 |
get_image_range "xen"
|
|
|
dcf580 |
do_param_setup
|
|
|
dcf580 |
@@ -1600,6 +1715,10 @@ do_start_daemon()
|
|
|
dcf580 |
OPD_ARGS="$OPD_ARGS --image=$IMAGE_FILTER"
|
|
|
dcf580 |
fi
|
|
|
dcf580 |
|
|
|
dcf580 |
+ if ! test -z "$PASSIVE_DOMAINS"; then
|
|
|
dcf580 |
+ OPD_ARGS="$OPD_ARGS --xen-passive-setup=$PASSIVE_SETUP_FILE"
|
|
|
dcf580 |
+ fi
|
|
|
dcf580 |
+
|
|
|
dcf580 |
if test -n "$VERBOSE"; then
|
|
|
dcf580 |
OPD_ARGS="$OPD_ARGS --verbose=$VERBOSE"
|
|
|
dcf580 |
fi
|
|
|
dcf580 |
@@ -1805,6 +1924,8 @@ do_save_session()
|
|
|
dcf580 |
fi
|
|
|
dcf580 |
|
|
|
dcf580 |
hup_daemon
|
|
|
dcf580 |
+
|
|
|
dcf580 |
+ rm -f /boot/domain-*-kernel /boot/domain-*-xen
|
|
|
dcf580 |
}
|
|
|
dcf580 |
|
|
|
dcf580 |
|
|
|
dcf580 |
@@ -1855,7 +1976,8 @@ do_operations()
|
|
|
dcf580 |
fi
|
|
|
dcf580 |
|
|
|
dcf580 |
if test "$SETUP" = "yes"; then
|
|
|
dcf580 |
- check_valid_args
|
|
|
dcf580 |
+ check_valid_vmlinux
|
|
|
dcf580 |
+ check_valid_xen
|
|
|
dcf580 |
do_save_setup
|
|
|
dcf580 |
fi
|
|
|
dcf580 |
|