diff options
| author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2017-09-03 18:07:36 -0400 |
|---|---|---|
| committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2017-09-03 18:07:36 -0400 |
| commit | d97561f461e4cb5b2e170bc33b466cffbddc8719 (patch) | |
| tree | 7be9749e40f2db48c0cebbc1d0eeadbda36c7ce9 | |
| parent | 4afbce7b39d2b326616f0a87a6246b1383237868 (diff) | |
| parent | d0e4a193c33adaa4f91128d5393aa3589c2f3e9e (diff) | |
Merge branch 'pm-tools'
* pm-tools:
tools/power/cpupower: allow running without cpu0
pm-graph: package makefile and man pages
pm-graph: AnalyzeBoot v2.1
pm-graph: AnalyzeSuspend v4.7
| -rw-r--r-- | tools/power/cpupower/utils/cpupower.c | 15 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/helpers/cpuid.c | 4 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/helpers/helpers.h | 5 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/helpers/misc.c | 2 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c | 4 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/idle_monitor/mperf_monitor.c | 3 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/idle_monitor/nhm_idle.c | 8 | ||||
| -rw-r--r-- | tools/power/cpupower/utils/idle_monitor/snb_idle.c | 4 | ||||
| -rw-r--r-- | tools/power/pm-graph/Makefile | 19 | ||||
| -rwxr-xr-x | tools/power/pm-graph/analyze_boot.py | 586 | ||||
| -rwxr-xr-x | tools/power/pm-graph/analyze_suspend.py | 534 | ||||
| -rw-r--r-- | tools/power/pm-graph/bootgraph.8 | 61 | ||||
| -rw-r--r-- | tools/power/pm-graph/sleepgraph.8 | 48 |
13 files changed, 880 insertions, 413 deletions
diff --git a/tools/power/cpupower/utils/cpupower.c b/tools/power/cpupower/utils/cpupower.c index 9ea914378985..2dccf4998599 100644 --- a/tools/power/cpupower/utils/cpupower.c +++ b/tools/power/cpupower/utils/cpupower.c | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include <string.h> | 12 | #include <string.h> |
| 13 | #include <unistd.h> | 13 | #include <unistd.h> |
| 14 | #include <errno.h> | 14 | #include <errno.h> |
| 15 | #include <sched.h> | ||
| 15 | #include <sys/types.h> | 16 | #include <sys/types.h> |
| 16 | #include <sys/stat.h> | 17 | #include <sys/stat.h> |
| 17 | #include <sys/utsname.h> | 18 | #include <sys/utsname.h> |
| @@ -31,6 +32,7 @@ static int cmd_help(int argc, const char **argv); | |||
| 31 | */ | 32 | */ |
| 32 | struct cpupower_cpu_info cpupower_cpu_info; | 33 | struct cpupower_cpu_info cpupower_cpu_info; |
| 33 | int run_as_root; | 34 | int run_as_root; |
| 35 | int base_cpu; | ||
| 34 | /* Affected cpus chosen by -c/--cpu param */ | 36 | /* Affected cpus chosen by -c/--cpu param */ |
| 35 | struct bitmask *cpus_chosen; | 37 | struct bitmask *cpus_chosen; |
| 36 | 38 | ||
| @@ -174,6 +176,7 @@ int main(int argc, const char *argv[]) | |||
| 174 | unsigned int i, ret; | 176 | unsigned int i, ret; |
| 175 | struct stat statbuf; | 177 | struct stat statbuf; |
| 176 | struct utsname uts; | 178 | struct utsname uts; |
| 179 | char pathname[32]; | ||
| 177 | 180 | ||
| 178 | cpus_chosen = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF)); | 181 | cpus_chosen = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF)); |
| 179 | 182 | ||
| @@ -198,17 +201,23 @@ int main(int argc, const char *argv[]) | |||
| 198 | argv[0] = cmd = "help"; | 201 | argv[0] = cmd = "help"; |
| 199 | } | 202 | } |
| 200 | 203 | ||
| 201 | get_cpu_info(0, &cpupower_cpu_info); | 204 | base_cpu = sched_getcpu(); |
| 205 | if (base_cpu < 0) { | ||
| 206 | fprintf(stderr, _("No valid cpus found.\n")); | ||
| 207 | return EXIT_FAILURE; | ||
| 208 | } | ||
| 209 | |||
| 210 | get_cpu_info(&cpupower_cpu_info); | ||
| 202 | run_as_root = !geteuid(); | 211 | run_as_root = !geteuid(); |
| 203 | if (run_as_root) { | 212 | if (run_as_root) { |
| 204 | ret = uname(&uts); | 213 | ret = uname(&uts); |
| 214 | sprintf(pathname, "/dev/cpu/%d/msr", base_cpu); | ||
| 205 | if (!ret && !strcmp(uts.machine, "x86_64") && | 215 | if (!ret && !strcmp(uts.machine, "x86_64") && |
| 206 | stat("/dev/cpu/0/msr", &statbuf) != 0) { | 216 | stat(pathname, &statbuf) != 0) { |
| 207 | if (system("modprobe msr") == -1) | 217 | if (system("modprobe msr") == -1) |
| 208 | fprintf(stderr, _("MSR access not available.\n")); | 218 | fprintf(stderr, _("MSR access not available.\n")); |
| 209 | } | 219 | } |
| 210 | } | 220 | } |
| 211 | |||
| 212 | 221 | ||
| 213 | for (i = 0; i < ARRAY_SIZE(commands); i++) { | 222 | for (i = 0; i < ARRAY_SIZE(commands); i++) { |
| 214 | struct cmd_struct *p = commands + i; | 223 | struct cmd_struct *p = commands + i; |
diff --git a/tools/power/cpupower/utils/helpers/cpuid.c b/tools/power/cpupower/utils/helpers/cpuid.c index 39c2c7d067bb..32d37c9be791 100644 --- a/tools/power/cpupower/utils/helpers/cpuid.c +++ b/tools/power/cpupower/utils/helpers/cpuid.c | |||
| @@ -42,7 +42,7 @@ cpuid_func(edx); | |||
| 42 | * | 42 | * |
| 43 | * TBD: Should there be a cpuid alternative for this if /proc is not mounted? | 43 | * TBD: Should there be a cpuid alternative for this if /proc is not mounted? |
| 44 | */ | 44 | */ |
| 45 | int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info) | 45 | int get_cpu_info(struct cpupower_cpu_info *cpu_info) |
| 46 | { | 46 | { |
| 47 | FILE *fp; | 47 | FILE *fp; |
| 48 | char value[64]; | 48 | char value[64]; |
| @@ -70,7 +70,7 @@ int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info) | |||
| 70 | if (!strncmp(value, "processor\t: ", 12)) | 70 | if (!strncmp(value, "processor\t: ", 12)) |
| 71 | sscanf(value, "processor\t: %u", &proc); | 71 | sscanf(value, "processor\t: %u", &proc); |
| 72 | 72 | ||
| 73 | if (proc != cpu) | 73 | if (proc != (unsigned int)base_cpu) |
| 74 | continue; | 74 | continue; |
| 75 | 75 | ||
| 76 | /* Get CPU vendor */ | 76 | /* Get CPU vendor */ |
diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index 799a18be60aa..41da392be448 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h | |||
| @@ -34,6 +34,7 @@ | |||
| 34 | /* Internationalization ****************************/ | 34 | /* Internationalization ****************************/ |
| 35 | 35 | ||
| 36 | extern int run_as_root; | 36 | extern int run_as_root; |
| 37 | extern int base_cpu; | ||
| 37 | extern struct bitmask *cpus_chosen; | 38 | extern struct bitmask *cpus_chosen; |
| 38 | 39 | ||
| 39 | /* Global verbose (-d) stuff *********************************/ | 40 | /* Global verbose (-d) stuff *********************************/ |
| @@ -87,11 +88,11 @@ struct cpupower_cpu_info { | |||
| 87 | * | 88 | * |
| 88 | * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo | 89 | * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo |
| 89 | * | 90 | * |
| 90 | * Returns 0 on success or a negativ error code | 91 | * Returns 0 on success or a negative error code |
| 91 | * Only used on x86, below global's struct values are zero/unknown on | 92 | * Only used on x86, below global's struct values are zero/unknown on |
| 92 | * other archs | 93 | * other archs |
| 93 | */ | 94 | */ |
| 94 | extern int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info); | 95 | extern int get_cpu_info(struct cpupower_cpu_info *cpu_info); |
| 95 | extern struct cpupower_cpu_info cpupower_cpu_info; | 96 | extern struct cpupower_cpu_info cpupower_cpu_info; |
| 96 | /* cpuid and cpuinfo helpers **************************/ | 97 | /* cpuid and cpuinfo helpers **************************/ |
| 97 | 98 | ||
diff --git a/tools/power/cpupower/utils/helpers/misc.c b/tools/power/cpupower/utils/helpers/misc.c index 601d719d4e60..a5e7ddf19dbd 100644 --- a/tools/power/cpupower/utils/helpers/misc.c +++ b/tools/power/cpupower/utils/helpers/misc.c | |||
| @@ -13,7 +13,7 @@ int cpufreq_has_boost_support(unsigned int cpu, int *support, int *active, | |||
| 13 | 13 | ||
| 14 | *support = *active = *states = 0; | 14 | *support = *active = *states = 0; |
| 15 | 15 | ||
| 16 | ret = get_cpu_info(0, &cpu_info); | 16 | ret = get_cpu_info(&cpu_info); |
| 17 | if (ret) | 17 | if (ret) |
| 18 | return ret; | 18 | return ret; |
| 19 | 19 | ||
diff --git a/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c b/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c index ebeaba6571a3..f794d6bbb7e9 100644 --- a/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c +++ b/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c | |||
| @@ -123,7 +123,7 @@ static int hsw_ext_start(void) | |||
| 123 | previous_count[num][cpu] = val; | 123 | previous_count[num][cpu] = val; |
| 124 | } | 124 | } |
| 125 | } | 125 | } |
| 126 | hsw_ext_get_count(TSC, &tsc_at_measure_start, 0); | 126 | hsw_ext_get_count(TSC, &tsc_at_measure_start, base_cpu); |
| 127 | return 0; | 127 | return 0; |
| 128 | } | 128 | } |
| 129 | 129 | ||
| @@ -132,7 +132,7 @@ static int hsw_ext_stop(void) | |||
| 132 | unsigned long long val; | 132 | unsigned long long val; |
| 133 | int num, cpu; | 133 | int num, cpu; |
| 134 | 134 | ||
| 135 | hsw_ext_get_count(TSC, &tsc_at_measure_end, 0); | 135 | hsw_ext_get_count(TSC, &tsc_at_measure_end, base_cpu); |
| 136 | 136 | ||
| 137 | for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { | 137 | for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { |
| 138 | for (cpu = 0; cpu < cpu_count; cpu++) { | 138 | for (cpu = 0; cpu < cpu_count; cpu++) { |
diff --git a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c index c83f1606970b..d7c2a6d13dea 100644 --- a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c +++ b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c | |||
| @@ -80,7 +80,8 @@ static int *is_valid; | |||
| 80 | static int mperf_get_tsc(unsigned long long *tsc) | 80 | static int mperf_get_tsc(unsigned long long *tsc) |
| 81 | { | 81 | { |
| 82 | int ret; | 82 | int ret; |
| 83 | ret = read_msr(0, MSR_TSC, tsc); | 83 | |
| 84 | ret = read_msr(base_cpu, MSR_TSC, tsc); | ||
| 84 | if (ret) | 85 | if (ret) |
| 85 | dprint("Reading TSC MSR failed, returning %llu\n", *tsc); | 86 | dprint("Reading TSC MSR failed, returning %llu\n", *tsc); |
| 86 | return ret; | 87 | return ret; |
diff --git a/tools/power/cpupower/utils/idle_monitor/nhm_idle.c b/tools/power/cpupower/utils/idle_monitor/nhm_idle.c index d2a91dd0d563..abf8cb5f7349 100644 --- a/tools/power/cpupower/utils/idle_monitor/nhm_idle.c +++ b/tools/power/cpupower/utils/idle_monitor/nhm_idle.c | |||
| @@ -129,7 +129,7 @@ static int nhm_start(void) | |||
| 129 | int num, cpu; | 129 | int num, cpu; |
| 130 | unsigned long long dbg, val; | 130 | unsigned long long dbg, val; |
| 131 | 131 | ||
| 132 | nhm_get_count(TSC, &tsc_at_measure_start, 0); | 132 | nhm_get_count(TSC, &tsc_at_measure_start, base_cpu); |
| 133 | 133 | ||
| 134 | for (num = 0; num < NHM_CSTATE_COUNT; num++) { | 134 | for (num = 0; num < NHM_CSTATE_COUNT; num++) { |
| 135 | for (cpu = 0; cpu < cpu_count; cpu++) { | 135 | for (cpu = 0; cpu < cpu_count; cpu++) { |
| @@ -137,7 +137,7 @@ static int nhm_start(void) | |||
| 137 | previous_count[num][cpu] = val; | 137 | previous_count[num][cpu] = val; |
| 138 | } | 138 | } |
| 139 | } | 139 | } |
| 140 | nhm_get_count(TSC, &dbg, 0); | 140 | nhm_get_count(TSC, &dbg, base_cpu); |
| 141 | dprint("TSC diff: %llu\n", dbg - tsc_at_measure_start); | 141 | dprint("TSC diff: %llu\n", dbg - tsc_at_measure_start); |
| 142 | return 0; | 142 | return 0; |
| 143 | } | 143 | } |
| @@ -148,7 +148,7 @@ static int nhm_stop(void) | |||
| 148 | unsigned long long dbg; | 148 | unsigned long long dbg; |
| 149 | int num, cpu; | 149 | int num, cpu; |
| 150 | 150 | ||
| 151 | nhm_get_count(TSC, &tsc_at_measure_end, 0); | 151 | nhm_get_count(TSC, &tsc_at_measure_end, base_cpu); |
| 152 | 152 | ||
| 153 | for (num = 0; num < NHM_CSTATE_COUNT; num++) { | 153 | for (num = 0; num < NHM_CSTATE_COUNT; num++) { |
| 154 | for (cpu = 0; cpu < cpu_count; cpu++) { | 154 | for (cpu = 0; cpu < cpu_count; cpu++) { |
| @@ -156,7 +156,7 @@ static int nhm_stop(void) | |||
| 156 | current_count[num][cpu] = val; | 156 | current_count[num][cpu] = val; |
| 157 | } | 157 | } |
| 158 | } | 158 | } |
| 159 | nhm_get_count(TSC, &dbg, 0); | 159 | nhm_get_count(TSC, &dbg, base_cpu); |
| 160 | dprint("TSC diff: %llu\n", dbg - tsc_at_measure_end); | 160 | dprint("TSC diff: %llu\n", dbg - tsc_at_measure_end); |
| 161 | 161 | ||
| 162 | return 0; | 162 | return 0; |
diff --git a/tools/power/cpupower/utils/idle_monitor/snb_idle.c b/tools/power/cpupower/utils/idle_monitor/snb_idle.c index efc8a69c9aba..a2b45219648d 100644 --- a/tools/power/cpupower/utils/idle_monitor/snb_idle.c +++ b/tools/power/cpupower/utils/idle_monitor/snb_idle.c | |||
| @@ -120,7 +120,7 @@ static int snb_start(void) | |||
| 120 | previous_count[num][cpu] = val; | 120 | previous_count[num][cpu] = val; |
| 121 | } | 121 | } |
| 122 | } | 122 | } |
| 123 | snb_get_count(TSC, &tsc_at_measure_start, 0); | 123 | snb_get_count(TSC, &tsc_at_measure_start, base_cpu); |
| 124 | return 0; | 124 | return 0; |
| 125 | } | 125 | } |
| 126 | 126 | ||
| @@ -129,7 +129,7 @@ static int snb_stop(void) | |||
| 129 | unsigned long long val; | 129 | unsigned long long val; |
| 130 | int num, cpu; | 130 | int num, cpu; |
| 131 | 131 | ||
| 132 | snb_get_count(TSC, &tsc_at_measure_end, 0); | 132 | snb_get_count(TSC, &tsc_at_measure_end, base_cpu); |
| 133 | 133 | ||
| 134 | for (num = 0; num < SNB_CSTATE_COUNT; num++) { | 134 | for (num = 0; num < SNB_CSTATE_COUNT; num++) { |
| 135 | for (cpu = 0; cpu < cpu_count; cpu++) { | 135 | for (cpu = 0; cpu < cpu_count; cpu++) { |
diff --git a/tools/power/pm-graph/Makefile b/tools/power/pm-graph/Makefile index 4d0ccc89e6c6..32f40eacdafe 100644 --- a/tools/power/pm-graph/Makefile +++ b/tools/power/pm-graph/Makefile | |||
| @@ -4,7 +4,7 @@ DESTDIR ?= | |||
| 4 | all: | 4 | all: |
| 5 | @echo "Nothing to build" | 5 | @echo "Nothing to build" |
| 6 | 6 | ||
| 7 | install : | 7 | install : uninstall |
| 8 | install -d $(DESTDIR)$(PREFIX)/lib/pm-graph | 8 | install -d $(DESTDIR)$(PREFIX)/lib/pm-graph |
| 9 | install analyze_suspend.py $(DESTDIR)$(PREFIX)/lib/pm-graph | 9 | install analyze_suspend.py $(DESTDIR)$(PREFIX)/lib/pm-graph |
| 10 | install analyze_boot.py $(DESTDIR)$(PREFIX)/lib/pm-graph | 10 | install analyze_boot.py $(DESTDIR)$(PREFIX)/lib/pm-graph |
| @@ -17,12 +17,15 @@ install : | |||
| 17 | install sleepgraph.8 $(DESTDIR)$(PREFIX)/share/man/man8 | 17 | install sleepgraph.8 $(DESTDIR)$(PREFIX)/share/man/man8 |
| 18 | 18 | ||
| 19 | uninstall : | 19 | uninstall : |
| 20 | rm $(DESTDIR)$(PREFIX)/share/man/man8/bootgraph.8 | 20 | rm -f $(DESTDIR)$(PREFIX)/share/man/man8/bootgraph.8 |
| 21 | rm $(DESTDIR)$(PREFIX)/share/man/man8/sleepgraph.8 | 21 | rm -f $(DESTDIR)$(PREFIX)/share/man/man8/sleepgraph.8 |
| 22 | 22 | ||
| 23 | rm $(DESTDIR)$(PREFIX)/bin/bootgraph | 23 | rm -f $(DESTDIR)$(PREFIX)/bin/bootgraph |
| 24 | rm $(DESTDIR)$(PREFIX)/bin/sleepgraph | 24 | rm -f $(DESTDIR)$(PREFIX)/bin/sleepgraph |
| 25 | 25 | ||
| 26 | rm $(DESTDIR)$(PREFIX)/lib/pm-graph/analyze_boot.py | 26 | rm -f $(DESTDIR)$(PREFIX)/lib/pm-graph/analyze_boot.py |
| 27 | rm $(DESTDIR)$(PREFIX)/lib/pm-graph/analyze_suspend.py | 27 | rm -f $(DESTDIR)$(PREFIX)/lib/pm-graph/analyze_suspend.py |
| 28 | rmdir $(DESTDIR)$(PREFIX)/lib/pm-graph | 28 | rm -f $(DESTDIR)$(PREFIX)/lib/pm-graph/*.pyc |
| 29 | if [ -d $(DESTDIR)$(PREFIX)/lib/pm-graph ] ; then \ | ||
| 30 | rmdir $(DESTDIR)$(PREFIX)/lib/pm-graph; \ | ||
| 31 | fi; | ||
diff --git a/tools/power/pm-graph/analyze_boot.py b/tools/power/pm-graph/analyze_boot.py index 3e1dcbbf1adc..e83df141a597 100755 --- a/tools/power/pm-graph/analyze_boot.py +++ b/tools/power/pm-graph/analyze_boot.py | |||
| @@ -42,7 +42,7 @@ import analyze_suspend as aslib | |||
| 42 | # store system values and test parameters | 42 | # store system values and test parameters |
| 43 | class SystemValues(aslib.SystemValues): | 43 | class SystemValues(aslib.SystemValues): |
| 44 | title = 'BootGraph' | 44 | title = 'BootGraph' |
| 45 | version = 2.0 | 45 | version = '2.1' |
| 46 | hostname = 'localhost' | 46 | hostname = 'localhost' |
| 47 | testtime = '' | 47 | testtime = '' |
| 48 | kernel = '' | 48 | kernel = '' |
| @@ -50,9 +50,14 @@ class SystemValues(aslib.SystemValues): | |||
| 50 | ftracefile = '' | 50 | ftracefile = '' |
| 51 | htmlfile = 'bootgraph.html' | 51 | htmlfile = 'bootgraph.html' |
| 52 | outfile = '' | 52 | outfile = '' |
| 53 | phoronix = False | 53 | testdir = '' |
| 54 | addlogs = False | 54 | testdirprefix = 'boot' |
| 55 | embedded = False | ||
| 56 | testlog = False | ||
| 57 | dmesglog = False | ||
| 58 | ftracelog = False | ||
| 55 | useftrace = False | 59 | useftrace = False |
| 60 | usecallgraph = False | ||
| 56 | usedevsrc = True | 61 | usedevsrc = True |
| 57 | suspendmode = 'boot' | 62 | suspendmode = 'boot' |
| 58 | max_graph_depth = 2 | 63 | max_graph_depth = 2 |
| @@ -61,10 +66,12 @@ class SystemValues(aslib.SystemValues): | |||
| 61 | manual = False | 66 | manual = False |
| 62 | iscronjob = False | 67 | iscronjob = False |
| 63 | timeformat = '%.6f' | 68 | timeformat = '%.6f' |
| 69 | bootloader = 'grub' | ||
| 70 | blexec = [] | ||
| 64 | def __init__(self): | 71 | def __init__(self): |
| 65 | if('LOG_FILE' in os.environ and 'TEST_RESULTS_IDENTIFIER' in os.environ): | 72 | if('LOG_FILE' in os.environ and 'TEST_RESULTS_IDENTIFIER' in os.environ): |
| 66 | self.phoronix = True | 73 | self.embedded = True |
| 67 | self.addlogs = True | 74 | self.dmesglog = True |
| 68 | self.outfile = os.environ['LOG_FILE'] | 75 | self.outfile = os.environ['LOG_FILE'] |
| 69 | self.htmlfile = os.environ['LOG_FILE'] | 76 | self.htmlfile = os.environ['LOG_FILE'] |
| 70 | self.hostname = platform.node() | 77 | self.hostname = platform.node() |
| @@ -76,42 +83,80 @@ class SystemValues(aslib.SystemValues): | |||
| 76 | self.kernel = self.kernelVersion(val) | 83 | self.kernel = self.kernelVersion(val) |
| 77 | else: | 84 | else: |
| 78 | self.kernel = 'unknown' | 85 | self.kernel = 'unknown' |
| 86 | self.testdir = datetime.now().strftime('boot-%y%m%d-%H%M%S') | ||
| 79 | def kernelVersion(self, msg): | 87 | def kernelVersion(self, msg): |
| 80 | return msg.split()[2] | 88 | return msg.split()[2] |
| 89 | def checkFtraceKernelVersion(self): | ||
| 90 | val = tuple(map(int, self.kernel.split('-')[0].split('.'))) | ||
| 91 | if val >= (4, 10, 0): | ||
| 92 | return True | ||
| 93 | return False | ||
| 81 | def kernelParams(self): | 94 | def kernelParams(self): |
| 82 | cmdline = 'initcall_debug log_buf_len=32M' | 95 | cmdline = 'initcall_debug log_buf_len=32M' |
| 83 | if self.useftrace: | 96 | if self.useftrace: |
| 84 | cmdline += ' trace_buf_size=128M trace_clock=global '\ | 97 | if self.cpucount > 0: |
| 98 | bs = min(self.memtotal / 2, 2*1024*1024) / self.cpucount | ||
| 99 | else: | ||
| 100 | bs = 131072 | ||
| 101 | cmdline += ' trace_buf_size=%dK trace_clock=global '\ | ||
| 85 | 'trace_options=nooverwrite,funcgraph-abstime,funcgraph-cpu,'\ | 102 | 'trace_options=nooverwrite,funcgraph-abstime,funcgraph-cpu,'\ |
| 86 | 'funcgraph-duration,funcgraph-proc,funcgraph-tail,'\ | 103 | 'funcgraph-duration,funcgraph-proc,funcgraph-tail,'\ |
| 87 | 'nofuncgraph-overhead,context-info,graph-time '\ | 104 | 'nofuncgraph-overhead,context-info,graph-time '\ |
| 88 | 'ftrace=function_graph '\ | 105 | 'ftrace=function_graph '\ |
| 89 | 'ftrace_graph_max_depth=%d '\ | 106 | 'ftrace_graph_max_depth=%d '\ |
| 90 | 'ftrace_graph_filter=%s' % \ | 107 | 'ftrace_graph_filter=%s' % \ |
| 91 | (self.max_graph_depth, self.graph_filter) | 108 | (bs, self.max_graph_depth, self.graph_filter) |
| 92 | return cmdline | 109 | return cmdline |
| 93 | def setGraphFilter(self, val): | 110 | def setGraphFilter(self, val): |
| 94 | fp = open(self.tpath+'available_filter_functions') | 111 | master = self.getBootFtraceFilterFunctions() |
| 95 | master = fp.read().split('\n') | 112 | fs = '' |
| 96 | fp.close() | ||
| 97 | for i in val.split(','): | 113 | for i in val.split(','): |
| 98 | func = i.strip() | 114 | func = i.strip() |
| 115 | if func == '': | ||
| 116 | doError('badly formatted filter function string') | ||
| 117 | if '[' in func or ']' in func: | ||
| 118 | doError('loadable module functions not allowed - "%s"' % func) | ||
| 119 | if ' ' in func: | ||
| 120 | doError('spaces found in filter functions - "%s"' % func) | ||
| 99 | if func not in master: | 121 | if func not in master: |
| 100 | doError('function "%s" not available for ftrace' % func) | 122 | doError('function "%s" not available for ftrace' % func) |
| 101 | self.graph_filter = val | 123 | if not fs: |
| 124 | fs = func | ||
| 125 | else: | ||
| 126 | fs += ','+func | ||
| 127 | if not fs: | ||
| 128 | doError('badly formatted filter function string') | ||
| 129 | self.graph_filter = fs | ||
| 130 | def getBootFtraceFilterFunctions(self): | ||
| 131 | self.rootCheck(True) | ||
| 132 | fp = open(self.tpath+'available_filter_functions') | ||
| 133 | fulllist = fp.read().split('\n') | ||
| 134 | fp.close() | ||
| 135 | list = [] | ||
| 136 | for i in fulllist: | ||
| 137 | if not i or ' ' in i or '[' in i or ']' in i: | ||
| 138 | continue | ||
| 139 | list.append(i) | ||
| 140 | return list | ||
| 141 | def myCronJob(self, line): | ||
| 142 | if '@reboot' not in line: | ||
| 143 | return False | ||
| 144 | if 'bootgraph' in line or 'analyze_boot.py' in line or '-cronjob' in line: | ||
| 145 | return True | ||
| 146 | return False | ||
| 102 | def cronjobCmdString(self): | 147 | def cronjobCmdString(self): |
| 103 | cmdline = '%s -cronjob' % os.path.abspath(sys.argv[0]) | 148 | cmdline = '%s -cronjob' % os.path.abspath(sys.argv[0]) |
| 104 | args = iter(sys.argv[1:]) | 149 | args = iter(sys.argv[1:]) |
| 105 | for arg in args: | 150 | for arg in args: |
| 106 | if arg in ['-h', '-v', '-cronjob', '-reboot']: | 151 | if arg in ['-h', '-v', '-cronjob', '-reboot']: |
| 107 | continue | 152 | continue |
| 108 | elif arg in ['-o', '-dmesg', '-ftrace', '-filter']: | 153 | elif arg in ['-o', '-dmesg', '-ftrace', '-func']: |
| 109 | args.next() | 154 | args.next() |
| 110 | continue | 155 | continue |
| 111 | cmdline += ' '+arg | 156 | cmdline += ' '+arg |
| 112 | if self.graph_filter != 'do_one_initcall': | 157 | if self.graph_filter != 'do_one_initcall': |
| 113 | cmdline += ' -filter "%s"' % self.graph_filter | 158 | cmdline += ' -func "%s"' % self.graph_filter |
| 114 | cmdline += ' -o "%s"' % os.path.abspath(self.htmlfile) | 159 | cmdline += ' -o "%s"' % os.path.abspath(self.testdir) |
| 115 | return cmdline | 160 | return cmdline |
| 116 | def manualRebootRequired(self): | 161 | def manualRebootRequired(self): |
| 117 | cmdline = self.kernelParams() | 162 | cmdline = self.kernelParams() |
| @@ -121,6 +166,39 @@ class SystemValues(aslib.SystemValues): | |||
| 121 | print '3. After reboot, re-run this tool with the same arguments but no command (w/o -reboot or -manual).\n' | 166 | print '3. After reboot, re-run this tool with the same arguments but no command (w/o -reboot or -manual).\n' |
| 122 | print 'CMDLINE="%s"' % cmdline | 167 | print 'CMDLINE="%s"' % cmdline |
| 123 | sys.exit() | 168 | sys.exit() |
| 169 | def getExec(self, cmd): | ||
| 170 | dirlist = ['/sbin', '/bin', '/usr/sbin', '/usr/bin', | ||
| 171 | '/usr/local/sbin', '/usr/local/bin'] | ||
| 172 | for path in dirlist: | ||
| 173 | cmdfull = os.path.join(path, cmd) | ||
| 174 | if os.path.exists(cmdfull): | ||
| 175 | return cmdfull | ||
| 176 | return '' | ||
| 177 | def blGrub(self): | ||
| 178 | blcmd = '' | ||
| 179 | for cmd in ['update-grub', 'grub-mkconfig', 'grub2-mkconfig']: | ||
| 180 | if blcmd: | ||
| 181 | break | ||
| 182 | blcmd = self.getExec(cmd) | ||
| 183 | if not blcmd: | ||
| 184 | doError('[GRUB] missing update command') | ||
| 185 | if not os.path.exists('/etc/default/grub'): | ||
| 186 | doError('[GRUB] missing /etc/default/grub') | ||
| 187 | if 'grub2' in blcmd: | ||
| 188 | cfg = '/boot/grub2/grub.cfg' | ||
| 189 | else: | ||
| 190 | cfg = '/boot/grub/grub.cfg' | ||
| 191 | if not os.path.exists(cfg): | ||
| 192 | doError('[GRUB] missing %s' % cfg) | ||
| 193 | if 'update-grub' in blcmd: | ||
| 194 | self.blexec = [blcmd] | ||
| 195 | else: | ||
| 196 | self.blexec = [blcmd, '-o', cfg] | ||
| 197 | def getBootLoader(self): | ||
| 198 | if self.bootloader == 'grub': | ||
| 199 | self.blGrub() | ||
| 200 | else: | ||
| 201 | doError('unknown boot loader: %s' % self.bootloader) | ||
| 124 | 202 | ||
| 125 | sysvals = SystemValues() | 203 | sysvals = SystemValues() |
| 126 | 204 | ||
| @@ -136,20 +214,23 @@ class Data(aslib.Data): | |||
| 136 | idstr = '' | 214 | idstr = '' |
| 137 | html_device_id = 0 | 215 | html_device_id = 0 |
| 138 | valid = False | 216 | valid = False |
| 139 | initstart = 0.0 | 217 | tUserMode = 0.0 |
| 140 | boottime = '' | 218 | boottime = '' |
| 141 | phases = ['boot'] | 219 | phases = ['kernel', 'user'] |
| 142 | do_one_initcall = False | 220 | do_one_initcall = False |
| 143 | def __init__(self, num): | 221 | def __init__(self, num): |
| 144 | self.testnumber = num | 222 | self.testnumber = num |
| 145 | self.idstr = 'a' | 223 | self.idstr = 'a' |
| 146 | self.dmesgtext = [] | 224 | self.dmesgtext = [] |
| 147 | self.dmesg = { | 225 | self.dmesg = { |
| 148 | 'boot': {'list': dict(), 'start': -1.0, 'end': -1.0, 'row': 0, 'color': '#dddddd'} | 226 | 'kernel': {'list': dict(), 'start': -1.0, 'end': -1.0, 'row': 0, |
| 227 | 'order': 0, 'color': 'linear-gradient(to bottom, #fff, #bcf)'}, | ||
| 228 | 'user': {'list': dict(), 'start': -1.0, 'end': -1.0, 'row': 0, | ||
| 229 | 'order': 1, 'color': '#fff'} | ||
| 149 | } | 230 | } |
| 150 | def deviceTopology(self): | 231 | def deviceTopology(self): |
| 151 | return '' | 232 | return '' |
| 152 | def newAction(self, phase, name, start, end, ret, ulen): | 233 | def newAction(self, phase, name, pid, start, end, ret, ulen): |
| 153 | # new device callback for a specific phase | 234 | # new device callback for a specific phase |
| 154 | self.html_device_id += 1 | 235 | self.html_device_id += 1 |
| 155 | devid = '%s%d' % (self.idstr, self.html_device_id) | 236 | devid = '%s%d' % (self.idstr, self.html_device_id) |
| @@ -163,41 +244,46 @@ class Data(aslib.Data): | |||
| 163 | name = '%s[%d]' % (origname, i) | 244 | name = '%s[%d]' % (origname, i) |
| 164 | i += 1 | 245 | i += 1 |
| 165 | list[name] = {'name': name, 'start': start, 'end': end, | 246 | list[name] = {'name': name, 'start': start, 'end': end, |
| 166 | 'pid': 0, 'length': length, 'row': 0, 'id': devid, | 247 | 'pid': pid, 'length': length, 'row': 0, 'id': devid, |
| 167 | 'ret': ret, 'ulen': ulen } | 248 | 'ret': ret, 'ulen': ulen } |
| 168 | return name | 249 | return name |
| 169 | def deviceMatch(self, cg): | 250 | def deviceMatch(self, pid, cg): |
| 170 | if cg.end - cg.start == 0: | 251 | if cg.end - cg.start == 0: |
| 171 | return True | 252 | return True |
| 172 | list = self.dmesg['boot']['list'] | 253 | for p in data.phases: |
| 173 | for devname in list: | 254 | list = self.dmesg[p]['list'] |
| 174 | dev = list[devname] | 255 | for devname in list: |
| 175 | if cg.name == 'do_one_initcall': | 256 | dev = list[devname] |
| 176 | if(cg.start <= dev['start'] and cg.end >= dev['end'] and dev['length'] > 0): | 257 | if pid != dev['pid']: |
| 177 | dev['ftrace'] = cg | 258 | continue |
| 178 | self.do_one_initcall = True | 259 | if cg.name == 'do_one_initcall': |
| 179 | return True | 260 | if(cg.start <= dev['start'] and cg.end >= dev['end'] and dev['length'] > 0): |
| 180 | else: | 261 | dev['ftrace'] = cg |
| 181 | if(cg.start > dev['start'] and cg.end < dev['end']): | 262 | self.do_one_initcall = True |
| 182 | if 'ftraces' not in dev: | 263 | return True |
| 183 | dev['ftraces'] = [] | 264 | else: |
| 184 | dev['ftraces'].append(cg) | 265 | if(cg.start > dev['start'] and cg.end < dev['end']): |
| 185 | return True | 266 | if 'ftraces' not in dev: |
| 267 | dev['ftraces'] = [] | ||
| 268 | dev['ftraces'].append(cg) | ||
| 269 | return True | ||
| 186 | return False | 270 | return False |
| 187 | 271 | ||
| 188 | # ----------------- FUNCTIONS -------------------- | 272 | # ----------------- FUNCTIONS -------------------- |
| 189 | 273 | ||
| 190 | # Function: loadKernelLog | 274 | # Function: parseKernelLog |
| 191 | # Description: | 275 | # Description: |
| 192 | # Load a raw kernel log from dmesg | 276 | # parse a kernel log for boot data |
| 193 | def loadKernelLog(): | 277 | def parseKernelLog(): |
| 278 | phase = 'kernel' | ||
| 194 | data = Data(0) | 279 | data = Data(0) |
| 195 | data.dmesg['boot']['start'] = data.start = ktime = 0.0 | 280 | data.dmesg['kernel']['start'] = data.start = ktime = 0.0 |
| 196 | sysvals.stamp = { | 281 | sysvals.stamp = { |
| 197 | 'time': datetime.now().strftime('%B %d %Y, %I:%M:%S %p'), | 282 | 'time': datetime.now().strftime('%B %d %Y, %I:%M:%S %p'), |
| 198 | 'host': sysvals.hostname, | 283 | 'host': sysvals.hostname, |
| 199 | 'mode': 'boot', 'kernel': ''} | 284 | 'mode': 'boot', 'kernel': ''} |
| 200 | 285 | ||
| 286 | tp = aslib.TestProps() | ||
| 201 | devtemp = dict() | 287 | devtemp = dict() |
| 202 | if(sysvals.dmesgfile): | 288 | if(sysvals.dmesgfile): |
| 203 | lf = open(sysvals.dmesgfile, 'r') | 289 | lf = open(sysvals.dmesgfile, 'r') |
| @@ -205,6 +291,13 @@ def loadKernelLog(): | |||
| 205 | lf = Popen('dmesg', stdout=PIPE).stdout | 291 | lf = Popen('dmesg', stdout=PIPE).stdout |
| 206 | for line in lf: | 292 | for line in lf: |
| 207 | line = line.replace('\r\n', '') | 293 | line = line.replace('\r\n', '') |
| 294 | # grab the stamp and sysinfo | ||
| 295 | if re.match(tp.stampfmt, line): | ||
| 296 | tp.stamp = line | ||
| 297 | continue | ||
| 298 | elif re.match(tp.sysinfofmt, line): | ||
| 299 | tp.sysinfo = line | ||
| 300 | continue | ||
| 208 | idx = line.find('[') | 301 | idx = line.find('[') |
| 209 | if idx > 1: | 302 | if idx > 1: |
| 210 | line = line[idx:] | 303 | line = line[idx:] |
| @@ -215,7 +308,6 @@ def loadKernelLog(): | |||
| 215 | if(ktime > 120): | 308 | if(ktime > 120): |
| 216 | break | 309 | break |
| 217 | msg = m.group('msg') | 310 | msg = m.group('msg') |
| 218 | data.end = data.initstart = ktime | ||
| 219 | data.dmesgtext.append(line) | 311 | data.dmesgtext.append(line) |
| 220 | if(ktime == 0.0 and re.match('^Linux version .*', msg)): | 312 | if(ktime == 0.0 and re.match('^Linux version .*', msg)): |
| 221 | if(not sysvals.stamp['kernel']): | 313 | if(not sysvals.stamp['kernel']): |
| @@ -228,43 +320,39 @@ def loadKernelLog(): | |||
| 228 | data.boottime = bt.strftime('%Y-%m-%d_%H:%M:%S') | 320 | data.boottime = bt.strftime('%Y-%m-%d_%H:%M:%S') |
| 229 | sysvals.stamp['time'] = bt.strftime('%B %d %Y, %I:%M:%S %p') | 321 | sysvals.stamp['time'] = bt.strftime('%B %d %Y, %I:%M:%S %p') |
| 230 | continue | 322 | continue |
| 231 | m = re.match('^calling *(?P<f>.*)\+.*', msg) | 323 | m = re.match('^calling *(?P<f>.*)\+.* @ (?P<p>[0-9]*)', msg) |
| 232 | if(m): | 324 | if(m): |
| 233 | devtemp[m.group('f')] = ktime | 325 | func = m.group('f') |
| 326 | pid = int(m.group('p')) | ||
| 327 | devtemp[func] = (ktime, pid) | ||
| 234 | continue | 328 | continue |
| 235 | m = re.match('^initcall *(?P<f>.*)\+.* returned (?P<r>.*) after (?P<t>.*) usecs', msg) | 329 | m = re.match('^initcall *(?P<f>.*)\+.* returned (?P<r>.*) after (?P<t>.*) usecs', msg) |
| 236 | if(m): | 330 | if(m): |
| 237 | data.valid = True | 331 | data.valid = True |
| 332 | data.end = ktime | ||
| 238 | f, r, t = m.group('f', 'r', 't') | 333 | f, r, t = m.group('f', 'r', 't') |
| 239 | if(f in devtemp): | 334 | if(f in devtemp): |
| 240 | data.newAction('boot', f, devtemp[f], ktime, int(r), int(t)) | 335 | start, pid = devtemp[f] |
| 241 | data.end = ktime | 336 | data.newAction(phase, f, pid, start, ktime, int(r), int(t)) |
| 242 | del devtemp[f] | 337 | del devtemp[f] |
| 243 | continue | 338 | continue |
| 244 | if(re.match('^Freeing unused kernel memory.*', msg)): | 339 | if(re.match('^Freeing unused kernel memory.*', msg)): |
| 245 | break | 340 | data.tUserMode = ktime |
| 246 | 341 | data.dmesg['kernel']['end'] = ktime | |
| 247 | data.dmesg['boot']['end'] = data.end | 342 | data.dmesg['user']['start'] = ktime |
| 343 | phase = 'user' | ||
| 344 | |||
| 345 | if tp.stamp: | ||
| 346 | sysvals.stamp = 0 | ||
| 347 | tp.parseStamp(data, sysvals) | ||
| 348 | data.dmesg['user']['end'] = data.end | ||
| 248 | lf.close() | 349 | lf.close() |
| 249 | return data | 350 | return data |
| 250 | 351 | ||
| 251 | # Function: loadTraceLog | 352 | # Function: parseTraceLog |
| 252 | # Description: | 353 | # Description: |
| 253 | # Check if trace is available and copy to a temp file | 354 | # Check if trace is available and copy to a temp file |
| 254 | def loadTraceLog(data): | 355 | def parseTraceLog(data): |
| 255 | # load the data to a temp file if none given | ||
| 256 | if not sysvals.ftracefile: | ||
| 257 | lib = aslib.sysvals | ||
| 258 | aslib.rootCheck(True) | ||
| 259 | if not lib.verifyFtrace(): | ||
| 260 | doError('ftrace not available') | ||
| 261 | if lib.fgetVal('current_tracer').strip() != 'function_graph': | ||
| 262 | doError('ftrace not configured for a boot callgraph') | ||
| 263 | sysvals.ftracefile = '/tmp/boot_ftrace.%s.txt' % os.getpid() | ||
| 264 | call('cat '+lib.tpath+'trace > '+sysvals.ftracefile, shell=True) | ||
| 265 | if not sysvals.ftracefile: | ||
| 266 | doError('No trace data available') | ||
| 267 | |||
| 268 | # parse the trace log | 356 | # parse the trace log |
| 269 | ftemp = dict() | 357 | ftemp = dict() |
| 270 | tp = aslib.TestProps() | 358 | tp = aslib.TestProps() |
| @@ -306,9 +394,29 @@ def loadTraceLog(data): | |||
| 306 | print('Sanity check failed for %s-%d' % (proc, pid)) | 394 | print('Sanity check failed for %s-%d' % (proc, pid)) |
| 307 | continue | 395 | continue |
| 308 | # match cg data to devices | 396 | # match cg data to devices |
| 309 | if not data.deviceMatch(cg): | 397 | if not data.deviceMatch(pid, cg): |
| 310 | print ' BAD: %s %s-%d [%f - %f]' % (cg.name, proc, pid, cg.start, cg.end) | 398 | print ' BAD: %s %s-%d [%f - %f]' % (cg.name, proc, pid, cg.start, cg.end) |
| 311 | 399 | ||
| 400 | # Function: retrieveLogs | ||
| 401 | # Description: | ||
| 402 | # Create copies of dmesg and/or ftrace for later processing | ||
| 403 | def retrieveLogs(): | ||
| 404 | # check ftrace is configured first | ||
| 405 | if sysvals.useftrace: | ||
| 406 | tracer = sysvals.fgetVal('current_tracer').strip() | ||
| 407 | if tracer != 'function_graph': | ||
| 408 | doError('ftrace not configured for a boot callgraph') | ||
| 409 | # create the folder and get dmesg | ||
| 410 | sysvals.systemInfo(aslib.dmidecode(sysvals.mempath)) | ||
| 411 | sysvals.initTestOutput('boot') | ||
| 412 | sysvals.writeDatafileHeader(sysvals.dmesgfile) | ||
| 413 | call('dmesg >> '+sysvals.dmesgfile, shell=True) | ||
| 414 | if not sysvals.useftrace: | ||
| 415 | return | ||
| 416 | # get ftrace | ||
| 417 | sysvals.writeDatafileHeader(sysvals.ftracefile) | ||
| 418 | call('cat '+sysvals.tpath+'trace >> '+sysvals.ftracefile, shell=True) | ||
| 419 | |||
| 312 | # Function: colorForName | 420 | # Function: colorForName |
| 313 | # Description: | 421 | # Description: |
| 314 | # Generate a repeatable color from a list for a given name | 422 | # Generate a repeatable color from a list for a given name |
| @@ -353,18 +461,19 @@ def cgOverview(cg, minlen): | |||
| 353 | # testruns: array of Data objects from parseKernelLog or parseTraceLog | 461 | # testruns: array of Data objects from parseKernelLog or parseTraceLog |
| 354 | # Output: | 462 | # Output: |
| 355 | # True if the html file was created, false if it failed | 463 | # True if the html file was created, false if it failed |
| 356 | def createBootGraph(data, embedded): | 464 | def createBootGraph(data): |
| 357 | # html function templates | 465 | # html function templates |
| 358 | html_srccall = '<div id={6} title="{5}" class="srccall" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;">{0}</div>\n' | 466 | html_srccall = '<div id={6} title="{5}" class="srccall" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;">{0}</div>\n' |
| 359 | html_timetotal = '<table class="time1">\n<tr>'\ | 467 | html_timetotal = '<table class="time1">\n<tr>'\ |
| 360 | '<td class="blue">Time from Kernel Boot to start of User Mode: <b>{0} ms</b></td>'\ | 468 | '<td class="blue">Init process starts @ <b>{0} ms</b></td>'\ |
| 469 | '<td class="blue">Last initcall ends @ <b>{1} ms</b></td>'\ | ||
| 361 | '</tr>\n</table>\n' | 470 | '</tr>\n</table>\n' |
| 362 | 471 | ||
| 363 | # device timeline | 472 | # device timeline |
| 364 | devtl = aslib.Timeline(100, 20) | 473 | devtl = aslib.Timeline(100, 20) |
| 365 | 474 | ||
| 366 | # write the test title and general info header | 475 | # write the test title and general info header |
| 367 | devtl.createHeader(sysvals, 'noftrace') | 476 | devtl.createHeader(sysvals) |
| 368 | 477 | ||
| 369 | # Generate the header for this timeline | 478 | # Generate the header for this timeline |
| 370 | t0 = data.start | 479 | t0 = data.start |
| @@ -373,84 +482,98 @@ def createBootGraph(data, embedded): | |||
| 373 | if(tTotal == 0): | 482 | if(tTotal == 0): |
| 374 | print('ERROR: No timeline data') | 483 | print('ERROR: No timeline data') |
| 375 | return False | 484 | return False |
| 376 | boot_time = '%.0f'%(tTotal*1000) | 485 | user_mode = '%.0f'%(data.tUserMode*1000) |
| 377 | devtl.html += html_timetotal.format(boot_time) | 486 | last_init = '%.0f'%(tTotal*1000) |
| 487 | devtl.html += html_timetotal.format(user_mode, last_init) | ||
| 378 | 488 | ||
| 379 | # determine the maximum number of rows we need to draw | 489 | # determine the maximum number of rows we need to draw |
| 380 | phase = 'boot' | ||
| 381 | list = data.dmesg[phase]['list'] | ||
| 382 | devlist = [] | 490 | devlist = [] |
| 383 | for devname in list: | 491 | for p in data.phases: |
| 384 | d = aslib.DevItem(0, phase, list[devname]) | 492 | list = data.dmesg[p]['list'] |
| 385 | devlist.append(d) | 493 | for devname in list: |
| 386 | devtl.getPhaseRows(devlist) | 494 | d = aslib.DevItem(0, p, list[devname]) |
| 495 | devlist.append(d) | ||
| 496 | devtl.getPhaseRows(devlist, 0, 'start') | ||
| 387 | devtl.calcTotalRows() | 497 | devtl.calcTotalRows() |
| 388 | 498 | ||
| 389 | # draw the timeline background | 499 | # draw the timeline background |
| 390 | devtl.createZoomBox() | 500 | devtl.createZoomBox() |
| 391 | boot = data.dmesg[phase] | 501 | devtl.html += devtl.html_tblock.format('boot', '0', '100', devtl.scaleH) |
| 392 | length = boot['end']-boot['start'] | 502 | for p in data.phases: |
| 393 | left = '%.3f' % (((boot['start']-t0)*100.0)/tTotal) | 503 | phase = data.dmesg[p] |
| 394 | width = '%.3f' % ((length*100.0)/tTotal) | 504 | length = phase['end']-phase['start'] |
| 395 | devtl.html += devtl.html_tblock.format(phase, left, width, devtl.scaleH) | 505 | left = '%.3f' % (((phase['start']-t0)*100.0)/tTotal) |
| 396 | devtl.html += devtl.html_phase.format('0', '100', \ | 506 | width = '%.3f' % ((length*100.0)/tTotal) |
| 397 | '%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \ | 507 | devtl.html += devtl.html_phase.format(left, width, \ |
| 398 | 'white', '') | 508 | '%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \ |
| 509 | phase['color'], '') | ||
| 399 | 510 | ||
| 400 | # draw the device timeline | 511 | # draw the device timeline |
| 401 | num = 0 | 512 | num = 0 |
| 402 | devstats = dict() | 513 | devstats = dict() |
| 403 | for devname in sorted(list): | 514 | for phase in data.phases: |
| 404 | cls, color = colorForName(devname) | 515 | list = data.dmesg[phase]['list'] |
| 405 | dev = list[devname] | 516 | for devname in sorted(list): |
| 406 | info = '@|%.3f|%.3f|%.3f|%d' % (dev['start']*1000.0, dev['end']*1000.0, | 517 | cls, color = colorForName(devname) |
| 407 | dev['ulen']/1000.0, dev['ret']) | 518 | dev = list[devname] |
| 408 | devstats[dev['id']] = {'info':info} | 519 | info = '@|%.3f|%.3f|%.3f|%d' % (dev['start']*1000.0, dev['end']*1000.0, |
| 409 | dev['color'] = color | 520 | dev['ulen']/1000.0, dev['ret']) |
| 410 | height = devtl.phaseRowHeight(0, phase, dev['row']) | 521 | devstats[dev['id']] = {'info':info} |
| 411 | top = '%.6f' % ((dev['row']*height) + devtl.scaleH) | 522 | dev['color'] = color |
| 412 | left = '%.6f' % (((dev['start']-t0)*100)/tTotal) | 523 | height = devtl.phaseRowHeight(0, phase, dev['row']) |
| 413 | width = '%.6f' % (((dev['end']-dev['start'])*100)/tTotal) | 524 | top = '%.6f' % ((dev['row']*height) + devtl.scaleH) |
| 414 | length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000) | 525 | left = '%.6f' % (((dev['start']-t0)*100)/tTotal) |
| 415 | devtl.html += devtl.html_device.format(dev['id'], | 526 | width = '%.6f' % (((dev['end']-dev['start'])*100)/tTotal) |
| 416 | devname+length+'kernel_mode', left, top, '%.3f'%height, | 527 | length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000) |
| 417 | width, devname, ' '+cls, '') | 528 | devtl.html += devtl.html_device.format(dev['id'], |
| 418 | rowtop = devtl.phaseRowTop(0, phase, dev['row']) | 529 | devname+length+phase+'_mode', left, top, '%.3f'%height, |
| 419 | height = '%.6f' % (devtl.rowH / 2) | 530 | width, devname, ' '+cls, '') |
| 420 | top = '%.6f' % (rowtop + devtl.scaleH + (devtl.rowH / 2)) | 531 | rowtop = devtl.phaseRowTop(0, phase, dev['row']) |
| 421 | if data.do_one_initcall: | 532 | height = '%.6f' % (devtl.rowH / 2) |
| 422 | if('ftrace' not in dev): | 533 | top = '%.6f' % (rowtop + devtl.scaleH + (devtl.rowH / 2)) |
| 534 | if data.do_one_initcall: | ||
| 535 | if('ftrace' not in dev): | ||
| 536 | continue | ||
| 537 | cg = dev['ftrace'] | ||
| 538 | large, stats = cgOverview(cg, 0.001) | ||
| 539 | devstats[dev['id']]['fstat'] = stats | ||
| 540 | for l in large: | ||
| 541 | left = '%f' % (((l.time-t0)*100)/tTotal) | ||
| 542 | width = '%f' % (l.length*100/tTotal) | ||
| 543 | title = '%s (%0.3fms)' % (l.name, l.length * 1000.0) | ||
| 544 | devtl.html += html_srccall.format(l.name, left, | ||
| 545 | top, height, width, title, 'x%d'%num) | ||
| 546 | num += 1 | ||
| 547 | continue | ||
| 548 | if('ftraces' not in dev): | ||
| 423 | continue | 549 | continue |
| 424 | cg = dev['ftrace'] | 550 | for cg in dev['ftraces']: |
| 425 | large, stats = cgOverview(cg, 0.001) | 551 | left = '%f' % (((cg.start-t0)*100)/tTotal) |
| 426 | devstats[dev['id']]['fstat'] = stats | 552 | width = '%f' % ((cg.end-cg.start)*100/tTotal) |
| 427 | for l in large: | 553 | cglen = (cg.end - cg.start) * 1000.0 |
| 428 | left = '%f' % (((l.time-t0)*100)/tTotal) | 554 | title = '%s (%0.3fms)' % (cg.name, cglen) |
| 429 | width = '%f' % (l.length*100/tTotal) | 555 | cg.id = 'x%d' % num |
| 430 | title = '%s (%0.3fms)' % (l.name, l.length * 1000.0) | 556 | devtl.html += html_srccall.format(cg.name, left, |
| 431 | devtl.html += html_srccall.format(l.name, left, | 557 | top, height, width, title, dev['id']+cg.id) |
| 432 | top, height, width, title, 'x%d'%num) | ||
| 433 | num += 1 | 558 | num += 1 |
| 434 | continue | ||
| 435 | if('ftraces' not in dev): | ||
| 436 | continue | ||
| 437 | for cg in dev['ftraces']: | ||
| 438 | left = '%f' % (((cg.start-t0)*100)/tTotal) | ||
| 439 | width = '%f' % ((cg.end-cg.start)*100/tTotal) | ||
| 440 | cglen = (cg.end - cg.start) * 1000.0 | ||
| 441 | title = '%s (%0.3fms)' % (cg.name, cglen) | ||
| 442 | cg.id = 'x%d' % num | ||
| 443 | devtl.html += html_srccall.format(cg.name, left, | ||
| 444 | top, height, width, title, dev['id']+cg.id) | ||
| 445 | num += 1 | ||
| 446 | 559 | ||
| 447 | # draw the time scale, try to make the number of labels readable | 560 | # draw the time scale, try to make the number of labels readable |
| 448 | devtl.createTimeScale(t0, tMax, tTotal, phase) | 561 | devtl.createTimeScale(t0, tMax, tTotal, 'boot') |
| 449 | devtl.html += '</div>\n' | 562 | devtl.html += '</div>\n' |
| 450 | 563 | ||
| 451 | # timeline is finished | 564 | # timeline is finished |
| 452 | devtl.html += '</div>\n</div>\n' | 565 | devtl.html += '</div>\n</div>\n' |
| 453 | 566 | ||
| 567 | # draw a legend which describes the phases by color | ||
| 568 | devtl.html += '<div class="legend">\n' | ||
| 569 | pdelta = 20.0 | ||
| 570 | pmargin = 36.0 | ||
| 571 | for phase in data.phases: | ||
| 572 | order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin) | ||
| 573 | devtl.html += devtl.html_legend.format(order, \ | ||
| 574 | data.dmesg[phase]['color'], phase+'_mode', phase[0]) | ||
| 575 | devtl.html += '</div>\n' | ||
| 576 | |||
| 454 | if(sysvals.outfile == sysvals.htmlfile): | 577 | if(sysvals.outfile == sysvals.htmlfile): |
| 455 | hf = open(sysvals.htmlfile, 'a') | 578 | hf = open(sysvals.htmlfile, 'a') |
| 456 | else: | 579 | else: |
| @@ -474,7 +597,7 @@ def createBootGraph(data, embedded): | |||
| 474 | .fstat td {text-align:left;width:35px;}\n\ | 597 | .fstat td {text-align:left;width:35px;}\n\ |
| 475 | .srccall {position:absolute;font-size:10px;z-index:7;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,#CCC,#969696);}\n\ | 598 | .srccall {position:absolute;font-size:10px;z-index:7;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,#CCC,#969696);}\n\ |
| 476 | .srccall:hover {color:white;font-weight:bold;border:1px solid white;}\n' | 599 | .srccall:hover {color:white;font-weight:bold;border:1px solid white;}\n' |
| 477 | if(not embedded): | 600 | if(not sysvals.embedded): |
| 478 | aslib.addCSS(hf, sysvals, 1, False, extra) | 601 | aslib.addCSS(hf, sysvals, 1, False, extra) |
| 479 | 602 | ||
| 480 | # write the device timeline | 603 | # write the device timeline |
| @@ -495,9 +618,11 @@ def createBootGraph(data, embedded): | |||
| 495 | html = \ | 618 | html = \ |
| 496 | '<div id="devicedetailtitle"></div>\n'\ | 619 | '<div id="devicedetailtitle"></div>\n'\ |
| 497 | '<div id="devicedetail" style="display:none;">\n'\ | 620 | '<div id="devicedetail" style="display:none;">\n'\ |
| 498 | '<div id="devicedetail0">\n'\ | 621 | '<div id="devicedetail0">\n' |
| 499 | '<div id="kernel_mode" class="phaselet" style="left:0%;width:100%;background:#DDDDDD"></div>\n'\ | 622 | for p in data.phases: |
| 500 | '</div>\n</div>\n'\ | 623 | phase = data.dmesg[p] |
| 624 | html += devtl.html_phaselet.format(p+'_mode', '0', '100', phase['color']) | ||
| 625 | html += '</div>\n</div>\n'\ | ||
| 501 | '<script type="text/javascript">\n'+statinfo+\ | 626 | '<script type="text/javascript">\n'+statinfo+\ |
| 502 | '</script>\n' | 627 | '</script>\n' |
| 503 | hf.write(html) | 628 | hf.write(html) |
| @@ -507,21 +632,21 @@ def createBootGraph(data, embedded): | |||
| 507 | aslib.addCallgraphs(sysvals, hf, data) | 632 | aslib.addCallgraphs(sysvals, hf, data) |
| 508 | 633 | ||
| 509 | # add the dmesg log as a hidden div | 634 | # add the dmesg log as a hidden div |
| 510 | if sysvals.addlogs: | 635 | if sysvals.dmesglog: |
| 511 | hf.write('<div id="dmesglog" style="display:none;">\n') | 636 | hf.write('<div id="dmesglog" style="display:none;">\n') |
| 512 | for line in data.dmesgtext: | 637 | for line in data.dmesgtext: |
| 513 | line = line.replace('<', '<').replace('>', '>') | 638 | line = line.replace('<', '<').replace('>', '>') |
| 514 | hf.write(line) | 639 | hf.write(line) |
| 515 | hf.write('</div>\n') | 640 | hf.write('</div>\n') |
| 516 | 641 | ||
| 517 | if(not embedded): | 642 | if(not sysvals.embedded): |
| 518 | # write the footer and close | 643 | # write the footer and close |
| 519 | aslib.addScriptCode(hf, [data]) | 644 | aslib.addScriptCode(hf, [data]) |
| 520 | hf.write('</body>\n</html>\n') | 645 | hf.write('</body>\n</html>\n') |
| 521 | else: | 646 | else: |
| 522 | # embedded out will be loaded in a page, skip the js | 647 | # embedded out will be loaded in a page, skip the js |
| 523 | hf.write('<div id=bounds style=display:none>%f,%f</div>' % \ | 648 | hf.write('<div id=bounds style=display:none>%f,%f</div>' % \ |
| 524 | (data.start*1000, data.initstart*1000)) | 649 | (data.start*1000, data.end*1000)) |
| 525 | hf.close() | 650 | hf.close() |
| 526 | return True | 651 | return True |
| 527 | 652 | ||
| @@ -533,17 +658,20 @@ def updateCron(restore=False): | |||
| 533 | if not restore: | 658 | if not restore: |
| 534 | sysvals.rootUser(True) | 659 | sysvals.rootUser(True) |
| 535 | crondir = '/var/spool/cron/crontabs/' | 660 | crondir = '/var/spool/cron/crontabs/' |
| 536 | cronfile = crondir+'root' | 661 | if not os.path.exists(crondir): |
| 537 | backfile = crondir+'root-analyze_boot-backup' | 662 | crondir = '/var/spool/cron/' |
| 538 | if not os.path.exists(crondir): | 663 | if not os.path.exists(crondir): |
| 539 | doError('%s not found' % crondir) | 664 | doError('%s not found' % crondir) |
| 540 | out = Popen(['which', 'crontab'], stdout=PIPE).stdout.read() | 665 | cronfile = crondir+'root' |
| 541 | if not out: | 666 | backfile = crondir+'root-analyze_boot-backup' |
| 667 | cmd = sysvals.getExec('crontab') | ||
| 668 | if not cmd: | ||
| 542 | doError('crontab not found') | 669 | doError('crontab not found') |
| 543 | # on restore: move the backup cron back into place | 670 | # on restore: move the backup cron back into place |
| 544 | if restore: | 671 | if restore: |
| 545 | if os.path.exists(backfile): | 672 | if os.path.exists(backfile): |
| 546 | shutil.move(backfile, cronfile) | 673 | shutil.move(backfile, cronfile) |
| 674 | call([cmd, cronfile]) | ||
| 547 | return | 675 | return |
| 548 | # backup current cron and install new one with reboot | 676 | # backup current cron and install new one with reboot |
| 549 | if os.path.exists(cronfile): | 677 | if os.path.exists(cronfile): |
| @@ -556,13 +684,13 @@ def updateCron(restore=False): | |||
| 556 | fp = open(backfile, 'r') | 684 | fp = open(backfile, 'r') |
| 557 | op = open(cronfile, 'w') | 685 | op = open(cronfile, 'w') |
| 558 | for line in fp: | 686 | for line in fp: |
| 559 | if '@reboot' not in line: | 687 | if not sysvals.myCronJob(line): |
| 560 | op.write(line) | 688 | op.write(line) |
| 561 | continue | 689 | continue |
| 562 | fp.close() | 690 | fp.close() |
| 563 | op.write('@reboot python %s\n' % sysvals.cronjobCmdString()) | 691 | op.write('@reboot python %s\n' % sysvals.cronjobCmdString()) |
| 564 | op.close() | 692 | op.close() |
| 565 | res = call('crontab %s' % cronfile, shell=True) | 693 | res = call([cmd, cronfile]) |
| 566 | except Exception, e: | 694 | except Exception, e: |
| 567 | print 'Exception: %s' % str(e) | 695 | print 'Exception: %s' % str(e) |
| 568 | shutil.move(backfile, cronfile) | 696 | shutil.move(backfile, cronfile) |
| @@ -577,25 +705,16 @@ def updateGrub(restore=False): | |||
| 577 | # call update-grub on restore | 705 | # call update-grub on restore |
| 578 | if restore: | 706 | if restore: |
| 579 | try: | 707 | try: |
| 580 | call(['update-grub'], stderr=PIPE, stdout=PIPE, | 708 | call(sysvals.blexec, stderr=PIPE, stdout=PIPE, |
| 581 | env={'PATH': '.:/sbin:/usr/sbin:/usr/bin:/sbin:/bin'}) | 709 | env={'PATH': '.:/sbin:/usr/sbin:/usr/bin:/sbin:/bin'}) |
| 582 | except Exception, e: | 710 | except Exception, e: |
| 583 | print 'Exception: %s\n' % str(e) | 711 | print 'Exception: %s\n' % str(e) |
| 584 | return | 712 | return |
| 585 | # verify we can do this | ||
| 586 | sysvals.rootUser(True) | ||
| 587 | grubfile = '/etc/default/grub' | ||
| 588 | if not os.path.exists(grubfile): | ||
| 589 | print 'ERROR: Unable to set the kernel parameters via grub.\n' | ||
| 590 | sysvals.manualRebootRequired() | ||
| 591 | out = Popen(['which', 'update-grub'], stdout=PIPE).stdout.read() | ||
| 592 | if not out: | ||
| 593 | print 'ERROR: Unable to set the kernel parameters via grub.\n' | ||
| 594 | sysvals.manualRebootRequired() | ||
| 595 | |||
| 596 | # extract the option and create a grub config without it | 713 | # extract the option and create a grub config without it |
| 714 | sysvals.rootUser(True) | ||
| 597 | tgtopt = 'GRUB_CMDLINE_LINUX_DEFAULT' | 715 | tgtopt = 'GRUB_CMDLINE_LINUX_DEFAULT' |
| 598 | cmdline = '' | 716 | cmdline = '' |
| 717 | grubfile = '/etc/default/grub' | ||
| 599 | tempfile = '/etc/default/grub.analyze_boot' | 718 | tempfile = '/etc/default/grub.analyze_boot' |
| 600 | shutil.move(grubfile, tempfile) | 719 | shutil.move(grubfile, tempfile) |
| 601 | res = -1 | 720 | res = -1 |
| @@ -622,7 +741,7 @@ def updateGrub(restore=False): | |||
| 622 | # if the target option value is in quotes, strip them | 741 | # if the target option value is in quotes, strip them |
| 623 | sp = '"' | 742 | sp = '"' |
| 624 | val = cmdline.strip() | 743 | val = cmdline.strip() |
| 625 | if val[0] == '\'' or val[0] == '"': | 744 | if val and (val[0] == '\'' or val[0] == '"'): |
| 626 | sp = val[0] | 745 | sp = val[0] |
| 627 | val = val.strip(sp) | 746 | val = val.strip(sp) |
| 628 | cmdline = val | 747 | cmdline = val |
| @@ -633,7 +752,7 @@ def updateGrub(restore=False): | |||
| 633 | # write out the updated target option | 752 | # write out the updated target option |
| 634 | op.write('\n%s=%s%s%s\n' % (tgtopt, sp, cmdline, sp)) | 753 | op.write('\n%s=%s%s%s\n' % (tgtopt, sp, cmdline, sp)) |
| 635 | op.close() | 754 | op.close() |
| 636 | res = call('update-grub') | 755 | res = call(sysvals.blexec) |
| 637 | os.remove(grubfile) | 756 | os.remove(grubfile) |
| 638 | except Exception, e: | 757 | except Exception, e: |
| 639 | print 'Exception: %s' % str(e) | 758 | print 'Exception: %s' % str(e) |
| @@ -641,10 +760,18 @@ def updateGrub(restore=False): | |||
| 641 | # cleanup | 760 | # cleanup |
| 642 | shutil.move(tempfile, grubfile) | 761 | shutil.move(tempfile, grubfile) |
| 643 | if res != 0: | 762 | if res != 0: |
| 644 | doError('update-grub failed') | 763 | doError('update grub failed') |
| 645 | 764 | ||
| 646 | # Function: doError | 765 | # Function: updateKernelParams |
| 647 | # Description: | 766 | # Description: |
| 767 | # update boot conf for all kernels with our parameters | ||
| 768 | def updateKernelParams(restore=False): | ||
| 769 | # find the boot loader | ||
| 770 | sysvals.getBootLoader() | ||
| 771 | if sysvals.bootloader == 'grub': | ||
| 772 | updateGrub(restore) | ||
| 773 | |||
| 774 | # Function: doError Description: | ||
| 648 | # generic error function for catastrphic failures | 775 | # generic error function for catastrphic failures |
| 649 | # Arguments: | 776 | # Arguments: |
| 650 | # msg: the error message to print | 777 | # msg: the error message to print |
| @@ -660,7 +787,7 @@ def doError(msg, help=False): | |||
| 660 | # print out the help text | 787 | # print out the help text |
| 661 | def printHelp(): | 788 | def printHelp(): |
| 662 | print('') | 789 | print('') |
| 663 | print('%s v%.1f' % (sysvals.title, sysvals.version)) | 790 | print('%s v%s' % (sysvals.title, sysvals.version)) |
| 664 | print('Usage: bootgraph <options> <command>') | 791 | print('Usage: bootgraph <options> <command>') |
| 665 | print('') | 792 | print('') |
| 666 | print('Description:') | 793 | print('Description:') |
| @@ -669,13 +796,19 @@ def printHelp(): | |||
| 669 | print(' the start of the init process.') | 796 | print(' the start of the init process.') |
| 670 | print('') | 797 | print('') |
| 671 | print(' If no specific command is given the tool reads the current dmesg') | 798 | print(' If no specific command is given the tool reads the current dmesg') |
| 672 | print(' and/or ftrace log and outputs bootgraph.html') | 799 | print(' and/or ftrace log and creates a timeline') |
| 800 | print('') | ||
| 801 | print(' Generates output files in subdirectory: boot-yymmdd-HHMMSS') | ||
| 802 | print(' HTML output: <hostname>_boot.html') | ||
| 803 | print(' raw dmesg output: <hostname>_boot_dmesg.txt') | ||
| 804 | print(' raw ftrace output: <hostname>_boot_ftrace.txt') | ||
| 673 | print('') | 805 | print('') |
| 674 | print('Options:') | 806 | print('Options:') |
| 675 | print(' -h Print this help text') | 807 | print(' -h Print this help text') |
| 676 | print(' -v Print the current tool version') | 808 | print(' -v Print the current tool version') |
| 677 | print(' -addlogs Add the dmesg log to the html output') | 809 | print(' -addlogs Add the dmesg log to the html output') |
| 678 | print(' -o file Html timeline name (default: bootgraph.html)') | 810 | print(' -o name Overrides the output subdirectory name when running a new test') |
| 811 | print(' default: boot-{date}-{time}') | ||
| 679 | print(' [advanced]') | 812 | print(' [advanced]') |
| 680 | print(' -f Use ftrace to add function detail (default: disabled)') | 813 | print(' -f Use ftrace to add function detail (default: disabled)') |
| 681 | print(' -callgraph Add callgraph detail, can be very large (default: disabled)') | 814 | print(' -callgraph Add callgraph detail, can be very large (default: disabled)') |
| @@ -683,13 +816,18 @@ def printHelp(): | |||
| 683 | print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)') | 816 | print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)') |
| 684 | print(' -timeprec N Number of significant digits in timestamps (0:S, 3:ms, [6:us])') | 817 | print(' -timeprec N Number of significant digits in timestamps (0:S, 3:ms, [6:us])') |
| 685 | print(' -expandcg pre-expand the callgraph data in the html output (default: disabled)') | 818 | print(' -expandcg pre-expand the callgraph data in the html output (default: disabled)') |
| 686 | print(' -filter list Limit ftrace to comma-delimited list of functions (default: do_one_initcall)') | 819 | print(' -func list Limit ftrace to comma-delimited list of functions (default: do_one_initcall)') |
| 687 | print(' [commands]') | 820 | print(' -cgfilter S Filter the callgraph output in the timeline') |
| 821 | print(' -bl name Use the following boot loader for kernel params (default: grub)') | ||
| 688 | print(' -reboot Reboot the machine automatically and generate a new timeline') | 822 | print(' -reboot Reboot the machine automatically and generate a new timeline') |
| 689 | print(' -manual Show the requirements to generate a new timeline manually') | 823 | print(' -manual Show the steps to generate a new timeline manually (used with -reboot)') |
| 690 | print(' -dmesg file Load a stored dmesg file (used with -ftrace)') | 824 | print('') |
| 691 | print(' -ftrace file Load a stored ftrace file (used with -dmesg)') | 825 | print('Other commands:') |
| 692 | print(' -flistall Print all functions capable of being captured in ftrace') | 826 | print(' -flistall Print all functions capable of being captured in ftrace') |
| 827 | print(' -sysinfo Print out system info extracted from BIOS') | ||
| 828 | print(' [redo]') | ||
| 829 | print(' -dmesg file Create HTML output using dmesg input (used with -ftrace)') | ||
| 830 | print(' -ftrace file Create HTML output using ftrace input (used with -dmesg)') | ||
| 693 | print('') | 831 | print('') |
| 694 | return True | 832 | return True |
| 695 | 833 | ||
| @@ -698,14 +836,15 @@ def printHelp(): | |||
| 698 | if __name__ == '__main__': | 836 | if __name__ == '__main__': |
| 699 | # loop through the command line arguments | 837 | # loop through the command line arguments |
| 700 | cmd = '' | 838 | cmd = '' |
| 701 | simplecmds = ['-updategrub', '-flistall'] | 839 | testrun = True |
| 840 | simplecmds = ['-sysinfo', '-kpupdate', '-flistall', '-checkbl'] | ||
| 702 | args = iter(sys.argv[1:]) | 841 | args = iter(sys.argv[1:]) |
| 703 | for arg in args: | 842 | for arg in args: |
| 704 | if(arg == '-h'): | 843 | if(arg == '-h'): |
| 705 | printHelp() | 844 | printHelp() |
| 706 | sys.exit() | 845 | sys.exit() |
| 707 | elif(arg == '-v'): | 846 | elif(arg == '-v'): |
| 708 | print("Version %.1f" % sysvals.version) | 847 | print("Version %s" % sysvals.version) |
| 709 | sys.exit() | 848 | sys.exit() |
| 710 | elif(arg in simplecmds): | 849 | elif(arg in simplecmds): |
| 711 | cmd = arg[1:] | 850 | cmd = arg[1:] |
| @@ -716,16 +855,32 @@ if __name__ == '__main__': | |||
| 716 | sysvals.usecallgraph = True | 855 | sysvals.usecallgraph = True |
| 717 | elif(arg == '-mincg'): | 856 | elif(arg == '-mincg'): |
| 718 | sysvals.mincglen = aslib.getArgFloat('-mincg', args, 0.0, 10000.0) | 857 | sysvals.mincglen = aslib.getArgFloat('-mincg', args, 0.0, 10000.0) |
| 858 | elif(arg == '-cgfilter'): | ||
| 859 | try: | ||
| 860 | val = args.next() | ||
| 861 | except: | ||
| 862 | doError('No callgraph functions supplied', True) | ||
| 863 | sysvals.setDeviceFilter(val) | ||
| 864 | elif(arg == '-bl'): | ||
| 865 | try: | ||
| 866 | val = args.next() | ||
| 867 | except: | ||
| 868 | doError('No boot loader name supplied', True) | ||
| 869 | if val.lower() not in ['grub']: | ||
| 870 | doError('Unknown boot loader: %s' % val, True) | ||
| 871 | sysvals.bootloader = val.lower() | ||
| 719 | elif(arg == '-timeprec'): | 872 | elif(arg == '-timeprec'): |
| 720 | sysvals.setPrecision(aslib.getArgInt('-timeprec', args, 0, 6)) | 873 | sysvals.setPrecision(aslib.getArgInt('-timeprec', args, 0, 6)) |
| 721 | elif(arg == '-maxdepth'): | 874 | elif(arg == '-maxdepth'): |
| 722 | sysvals.max_graph_depth = aslib.getArgInt('-maxdepth', args, 0, 1000) | 875 | sysvals.max_graph_depth = aslib.getArgInt('-maxdepth', args, 0, 1000) |
| 723 | elif(arg == '-filter'): | 876 | elif(arg == '-func'): |
| 724 | try: | 877 | try: |
| 725 | val = args.next() | 878 | val = args.next() |
| 726 | except: | 879 | except: |
| 727 | doError('No filter functions supplied', True) | 880 | doError('No filter functions supplied', True) |
| 728 | aslib.rootCheck(True) | 881 | sysvals.useftrace = True |
| 882 | sysvals.usecallgraph = True | ||
| 883 | sysvals.rootCheck(True) | ||
| 729 | sysvals.setGraphFilter(val) | 884 | sysvals.setGraphFilter(val) |
| 730 | elif(arg == '-ftrace'): | 885 | elif(arg == '-ftrace'): |
| 731 | try: | 886 | try: |
| @@ -734,9 +889,10 @@ if __name__ == '__main__': | |||
| 734 | doError('No ftrace file supplied', True) | 889 | doError('No ftrace file supplied', True) |
| 735 | if(os.path.exists(val) == False): | 890 | if(os.path.exists(val) == False): |
| 736 | doError('%s does not exist' % val) | 891 | doError('%s does not exist' % val) |
| 892 | testrun = False | ||
| 737 | sysvals.ftracefile = val | 893 | sysvals.ftracefile = val |
| 738 | elif(arg == '-addlogs'): | 894 | elif(arg == '-addlogs'): |
| 739 | sysvals.addlogs = True | 895 | sysvals.dmesglog = True |
| 740 | elif(arg == '-expandcg'): | 896 | elif(arg == '-expandcg'): |
| 741 | sysvals.cgexp = True | 897 | sysvals.cgexp = True |
| 742 | elif(arg == '-dmesg'): | 898 | elif(arg == '-dmesg'): |
| @@ -748,18 +904,15 @@ if __name__ == '__main__': | |||
| 748 | doError('%s does not exist' % val) | 904 | doError('%s does not exist' % val) |
| 749 | if(sysvals.htmlfile == val or sysvals.outfile == val): | 905 | if(sysvals.htmlfile == val or sysvals.outfile == val): |
| 750 | doError('Output filename collision') | 906 | doError('Output filename collision') |
| 907 | testrun = False | ||
| 751 | sysvals.dmesgfile = val | 908 | sysvals.dmesgfile = val |
| 752 | elif(arg == '-o'): | 909 | elif(arg == '-o'): |
| 753 | try: | 910 | try: |
| 754 | val = args.next() | 911 | val = args.next() |
| 755 | except: | 912 | except: |
| 756 | doError('No HTML filename supplied', True) | 913 | doError('No subdirectory name supplied', True) |
| 757 | if(sysvals.dmesgfile == val or sysvals.ftracefile == val): | 914 | sysvals.testdir = sysvals.setOutputFolder(val) |
| 758 | doError('Output filename collision') | ||
| 759 | sysvals.htmlfile = val | ||
| 760 | elif(arg == '-reboot'): | 915 | elif(arg == '-reboot'): |
| 761 | if sysvals.iscronjob: | ||
| 762 | doError('-reboot and -cronjob are incompatible') | ||
| 763 | sysvals.reboot = True | 916 | sysvals.reboot = True |
| 764 | elif(arg == '-manual'): | 917 | elif(arg == '-manual'): |
| 765 | sysvals.reboot = True | 918 | sysvals.reboot = True |
| @@ -767,58 +920,93 @@ if __name__ == '__main__': | |||
| 767 | # remaining options are only for cron job use | 920 | # remaining options are only for cron job use |
| 768 | elif(arg == '-cronjob'): | 921 | elif(arg == '-cronjob'): |
| 769 | sysvals.iscronjob = True | 922 | sysvals.iscronjob = True |
| 770 | if sysvals.reboot: | ||
| 771 | doError('-reboot and -cronjob are incompatible') | ||
| 772 | else: | 923 | else: |
| 773 | doError('Invalid argument: '+arg, True) | 924 | doError('Invalid argument: '+arg, True) |
| 774 | 925 | ||
| 926 | # compatibility errors and access checks | ||
| 927 | if(sysvals.iscronjob and (sysvals.reboot or \ | ||
| 928 | sysvals.dmesgfile or sysvals.ftracefile or cmd)): | ||
| 929 | doError('-cronjob is meant for batch purposes only') | ||
| 930 | if(sysvals.reboot and (sysvals.dmesgfile or sysvals.ftracefile)): | ||
| 931 | doError('-reboot and -dmesg/-ftrace are incompatible') | ||
| 932 | if cmd or sysvals.reboot or sysvals.iscronjob or testrun: | ||
| 933 | sysvals.rootCheck(True) | ||
| 934 | if (testrun and sysvals.useftrace) or cmd == 'flistall': | ||
| 935 | if not sysvals.verifyFtrace(): | ||
| 936 | doError('Ftrace is not properly enabled') | ||
| 937 | |||
| 938 | # run utility commands | ||
| 939 | sysvals.cpuInfo() | ||
| 775 | if cmd != '': | 940 | if cmd != '': |
| 776 | if cmd == 'updategrub': | 941 | if cmd == 'kpupdate': |
| 777 | updateGrub() | 942 | updateKernelParams() |
| 778 | elif cmd == 'flistall': | 943 | elif cmd == 'flistall': |
| 779 | sysvals.getFtraceFilterFunctions(False) | 944 | for f in sysvals.getBootFtraceFilterFunctions(): |
| 945 | print f | ||
| 946 | elif cmd == 'checkbl': | ||
| 947 | sysvals.getBootLoader() | ||
| 948 | print 'Boot Loader: %s\n%s' % (sysvals.bootloader, sysvals.blexec) | ||
| 949 | elif(cmd == 'sysinfo'): | ||
| 950 | sysvals.printSystemInfo() | ||
| 780 | sys.exit() | 951 | sys.exit() |
| 781 | 952 | ||
| 782 | # update grub, setup a cronjob, and reboot | 953 | # reboot: update grub, setup a cronjob, and reboot |
| 783 | if sysvals.reboot: | 954 | if sysvals.reboot: |
| 955 | if (sysvals.useftrace or sysvals.usecallgraph) and \ | ||
| 956 | not sysvals.checkFtraceKernelVersion(): | ||
| 957 | doError('Ftrace functionality requires kernel v4.10 or newer') | ||
| 784 | if not sysvals.manual: | 958 | if not sysvals.manual: |
| 785 | updateGrub() | 959 | updateKernelParams() |
| 786 | updateCron() | 960 | updateCron() |
| 787 | call('reboot') | 961 | call('reboot') |
| 788 | else: | 962 | else: |
| 789 | sysvals.manualRebootRequired() | 963 | sysvals.manualRebootRequired() |
| 790 | sys.exit() | 964 | sys.exit() |
| 791 | 965 | ||
| 792 | # disable the cronjob | 966 | # cronjob: remove the cronjob, grub changes, and disable ftrace |
| 793 | if sysvals.iscronjob: | 967 | if sysvals.iscronjob: |
| 794 | updateCron(True) | 968 | updateCron(True) |
| 795 | updateGrub(True) | 969 | updateKernelParams(True) |
| 970 | try: | ||
| 971 | sysvals.fsetVal('0', 'tracing_on') | ||
| 972 | except: | ||
| 973 | pass | ||
| 796 | 974 | ||
| 797 | data = loadKernelLog() | 975 | # testrun: generate copies of the logs |
| 798 | if sysvals.useftrace: | 976 | if testrun: |
| 799 | loadTraceLog(data) | 977 | retrieveLogs() |
| 800 | if sysvals.iscronjob: | 978 | else: |
| 801 | try: | 979 | sysvals.setOutputFile() |
| 802 | sysvals.fsetVal('0', 'tracing_on') | ||
| 803 | except: | ||
| 804 | pass | ||
| 805 | 980 | ||
| 806 | if(sysvals.outfile and sysvals.phoronix): | 981 | # process the log data |
| 807 | fp = open(sysvals.outfile, 'w') | 982 | if sysvals.dmesgfile: |
| 808 | fp.write('pass %s initstart %.3f end %.3f boot %s\n' % | 983 | data = parseKernelLog() |
| 809 | (data.valid, data.initstart*1000, data.end*1000, data.boottime)) | 984 | if(not data.valid): |
| 810 | fp.close() | ||
| 811 | if(not data.valid): | ||
| 812 | if sysvals.dmesgfile: | ||
| 813 | doError('No initcall data found in %s' % sysvals.dmesgfile) | 985 | doError('No initcall data found in %s' % sysvals.dmesgfile) |
| 814 | else: | 986 | if sysvals.useftrace and sysvals.ftracefile: |
| 815 | doError('No initcall data found, is initcall_debug enabled?') | 987 | parseTraceLog(data) |
| 988 | else: | ||
| 989 | doError('dmesg file required') | ||
| 816 | 990 | ||
| 817 | print(' Host: %s' % sysvals.hostname) | 991 | print(' Host: %s' % sysvals.hostname) |
| 818 | print(' Test time: %s' % sysvals.testtime) | 992 | print(' Test time: %s' % sysvals.testtime) |
| 819 | print(' Boot time: %s' % data.boottime) | 993 | print(' Boot time: %s' % data.boottime) |
| 820 | print('Kernel Version: %s' % sysvals.kernel) | 994 | print('Kernel Version: %s' % sysvals.kernel) |
| 821 | print(' Kernel start: %.3f' % (data.start * 1000)) | 995 | print(' Kernel start: %.3f' % (data.start * 1000)) |
| 822 | print(' init start: %.3f' % (data.initstart * 1000)) | 996 | print('Usermode start: %.3f' % (data.tUserMode * 1000)) |
| 997 | print('Last Init Call: %.3f' % (data.end * 1000)) | ||
| 998 | |||
| 999 | # handle embedded output logs | ||
| 1000 | if(sysvals.outfile and sysvals.embedded): | ||
| 1001 | fp = open(sysvals.outfile, 'w') | ||
| 1002 | fp.write('pass %s initstart %.3f end %.3f boot %s\n' % | ||
| 1003 | (data.valid, data.tUserMode*1000, data.end*1000, data.boottime)) | ||
| 1004 | fp.close() | ||
| 1005 | |||
| 1006 | createBootGraph(data) | ||
| 823 | 1007 | ||
| 824 | createBootGraph(data, sysvals.phoronix) | 1008 | # if running as root, change output dir owner to sudo_user |
| 1009 | if testrun and os.path.isdir(sysvals.testdir) and \ | ||
| 1010 | os.getuid() == 0 and 'SUDO_USER' in os.environ: | ||
| 1011 | cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1' | ||
| 1012 | call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True) | ||
diff --git a/tools/power/pm-graph/analyze_suspend.py b/tools/power/pm-graph/analyze_suspend.py index a9206e67fc1f..1b60fe203741 100755 --- a/tools/power/pm-graph/analyze_suspend.py +++ b/tools/power/pm-graph/analyze_suspend.py | |||
| @@ -68,10 +68,12 @@ from subprocess import call, Popen, PIPE | |||
| 68 | # store system values and test parameters | 68 | # store system values and test parameters |
| 69 | class SystemValues: | 69 | class SystemValues: |
| 70 | title = 'SleepGraph' | 70 | title = 'SleepGraph' |
| 71 | version = '4.6' | 71 | version = '4.7' |
| 72 | ansi = False | 72 | ansi = False |
| 73 | verbose = False | 73 | verbose = False |
| 74 | addlogs = False | 74 | testlog = True |
| 75 | dmesglog = False | ||
| 76 | ftracelog = False | ||
| 75 | mindevlen = 0.0 | 77 | mindevlen = 0.0 |
| 76 | mincglen = 0.0 | 78 | mincglen = 0.0 |
| 77 | cgphase = '' | 79 | cgphase = '' |
| @@ -79,10 +81,11 @@ class SystemValues: | |||
| 79 | max_graph_depth = 0 | 81 | max_graph_depth = 0 |
| 80 | callloopmaxgap = 0.0001 | 82 | callloopmaxgap = 0.0001 |
| 81 | callloopmaxlen = 0.005 | 83 | callloopmaxlen = 0.005 |
| 84 | cpucount = 0 | ||
| 85 | memtotal = 204800 | ||
| 82 | srgap = 0 | 86 | srgap = 0 |
| 83 | cgexp = False | 87 | cgexp = False |
| 84 | outdir = '' | 88 | testdir = '' |
| 85 | testdir = '.' | ||
| 86 | tpath = '/sys/kernel/debug/tracing/' | 89 | tpath = '/sys/kernel/debug/tracing/' |
| 87 | fpdtpath = '/sys/firmware/acpi/tables/FPDT' | 90 | fpdtpath = '/sys/firmware/acpi/tables/FPDT' |
| 88 | epath = '/sys/kernel/debug/tracing/events/power/' | 91 | epath = '/sys/kernel/debug/tracing/events/power/' |
| @@ -95,14 +98,17 @@ class SystemValues: | |||
| 95 | testcommand = '' | 98 | testcommand = '' |
| 96 | mempath = '/dev/mem' | 99 | mempath = '/dev/mem' |
| 97 | powerfile = '/sys/power/state' | 100 | powerfile = '/sys/power/state' |
| 101 | mempowerfile = '/sys/power/mem_sleep' | ||
| 98 | suspendmode = 'mem' | 102 | suspendmode = 'mem' |
| 103 | memmode = '' | ||
| 99 | hostname = 'localhost' | 104 | hostname = 'localhost' |
| 100 | prefix = 'test' | 105 | prefix = 'test' |
| 101 | teststamp = '' | 106 | teststamp = '' |
| 107 | sysstamp = '' | ||
| 102 | dmesgstart = 0.0 | 108 | dmesgstart = 0.0 |
| 103 | dmesgfile = '' | 109 | dmesgfile = '' |
| 104 | ftracefile = '' | 110 | ftracefile = '' |
| 105 | htmlfile = '' | 111 | htmlfile = 'output.html' |
| 106 | embedded = False | 112 | embedded = False |
| 107 | rtcwake = True | 113 | rtcwake = True |
| 108 | rtcwaketime = 15 | 114 | rtcwaketime = 15 |
| @@ -127,9 +133,6 @@ class SystemValues: | |||
| 127 | devpropfmt = '# Device Properties: .*' | 133 | devpropfmt = '# Device Properties: .*' |
| 128 | tracertypefmt = '# tracer: (?P<t>.*)' | 134 | tracertypefmt = '# tracer: (?P<t>.*)' |
| 129 | firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$' | 135 | firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$' |
| 130 | stampfmt = '# suspend-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\ | ||
| 131 | '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\ | ||
| 132 | ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$' | ||
| 133 | tracefuncs = { | 136 | tracefuncs = { |
| 134 | 'sys_sync': dict(), | 137 | 'sys_sync': dict(), |
| 135 | 'pm_prepare_console': dict(), | 138 | 'pm_prepare_console': dict(), |
| @@ -218,7 +221,7 @@ class SystemValues: | |||
| 218 | # if this is a phoronix test run, set some default options | 221 | # if this is a phoronix test run, set some default options |
| 219 | if('LOG_FILE' in os.environ and 'TEST_RESULTS_IDENTIFIER' in os.environ): | 222 | if('LOG_FILE' in os.environ and 'TEST_RESULTS_IDENTIFIER' in os.environ): |
| 220 | self.embedded = True | 223 | self.embedded = True |
| 221 | self.addlogs = True | 224 | self.dmesglog = self.ftracelog = True |
| 222 | self.htmlfile = os.environ['LOG_FILE'] | 225 | self.htmlfile = os.environ['LOG_FILE'] |
| 223 | self.archargs = 'args_'+platform.machine() | 226 | self.archargs = 'args_'+platform.machine() |
| 224 | self.hostname = platform.node() | 227 | self.hostname = platform.node() |
| @@ -233,6 +236,13 @@ class SystemValues: | |||
| 233 | self.rtcpath = rtc | 236 | self.rtcpath = rtc |
| 234 | if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()): | 237 | if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()): |
| 235 | self.ansi = True | 238 | self.ansi = True |
| 239 | self.testdir = datetime.now().strftime('suspend-%y%m%d-%H%M%S') | ||
| 240 | def rootCheck(self, fatal=True): | ||
| 241 | if(os.access(self.powerfile, os.W_OK)): | ||
| 242 | return True | ||
| 243 | if fatal: | ||
| 244 | doError('This command requires sysfs mount and root access') | ||
| 245 | return False | ||
| 236 | def rootUser(self, fatal=False): | 246 | def rootUser(self, fatal=False): |
| 237 | if 'USER' in os.environ and os.environ['USER'] == 'root': | 247 | if 'USER' in os.environ and os.environ['USER'] == 'root': |
| 238 | return True | 248 | return True |
| @@ -249,30 +259,60 @@ class SystemValues: | |||
| 249 | args['date'] = n.strftime('%y%m%d') | 259 | args['date'] = n.strftime('%y%m%d') |
| 250 | args['time'] = n.strftime('%H%M%S') | 260 | args['time'] = n.strftime('%H%M%S') |
| 251 | args['hostname'] = self.hostname | 261 | args['hostname'] = self.hostname |
| 252 | self.outdir = value.format(**args) | 262 | return value.format(**args) |
| 253 | def setOutputFile(self): | 263 | def setOutputFile(self): |
| 254 | if((self.htmlfile == '') and (self.dmesgfile != '')): | 264 | if self.dmesgfile != '': |
| 255 | m = re.match('(?P<name>.*)_dmesg\.txt$', self.dmesgfile) | 265 | m = re.match('(?P<name>.*)_dmesg\.txt$', self.dmesgfile) |
| 256 | if(m): | 266 | if(m): |
| 257 | self.htmlfile = m.group('name')+'.html' | 267 | self.htmlfile = m.group('name')+'.html' |
| 258 | if((self.htmlfile == '') and (self.ftracefile != '')): | 268 | if self.ftracefile != '': |
| 259 | m = re.match('(?P<name>.*)_ftrace\.txt$', self.ftracefile) | 269 | m = re.match('(?P<name>.*)_ftrace\.txt$', self.ftracefile) |
| 260 | if(m): | 270 | if(m): |
| 261 | self.htmlfile = m.group('name')+'.html' | 271 | self.htmlfile = m.group('name')+'.html' |
| 262 | if(self.htmlfile == ''): | 272 | def systemInfo(self, info): |
| 263 | self.htmlfile = 'output.html' | 273 | p = c = m = b = '' |
| 264 | def initTestOutput(self, subdir, testpath=''): | 274 | if 'baseboard-manufacturer' in info: |
| 275 | m = info['baseboard-manufacturer'] | ||
| 276 | elif 'system-manufacturer' in info: | ||
| 277 | m = info['system-manufacturer'] | ||
| 278 | if 'baseboard-product-name' in info: | ||
| 279 | p = info['baseboard-product-name'] | ||
| 280 | elif 'system-product-name' in info: | ||
| 281 | p = info['system-product-name'] | ||
| 282 | if 'processor-version' in info: | ||
| 283 | c = info['processor-version'] | ||
| 284 | if 'bios-version' in info: | ||
| 285 | b = info['bios-version'] | ||
| 286 | self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | numcpu:%d | memsz:%d' % \ | ||
| 287 | (m, p, c, b, self.cpucount, self.memtotal) | ||
| 288 | def printSystemInfo(self): | ||
| 289 | self.rootCheck(True) | ||
| 290 | out = dmidecode(self.mempath, True) | ||
| 291 | fmt = '%-24s: %s' | ||
| 292 | for name in sorted(out): | ||
| 293 | print fmt % (name, out[name]) | ||
| 294 | print fmt % ('cpucount', ('%d' % self.cpucount)) | ||
| 295 | print fmt % ('memtotal', ('%d kB' % self.memtotal)) | ||
| 296 | def cpuInfo(self): | ||
| 297 | self.cpucount = 0 | ||
| 298 | fp = open('/proc/cpuinfo', 'r') | ||
| 299 | for line in fp: | ||
| 300 | if re.match('^processor[ \t]*:[ \t]*[0-9]*', line): | ||
| 301 | self.cpucount += 1 | ||
| 302 | fp.close() | ||
| 303 | fp = open('/proc/meminfo', 'r') | ||
| 304 | for line in fp: | ||
| 305 | m = re.match('^MemTotal:[ \t]*(?P<sz>[0-9]*) *kB', line) | ||
| 306 | if m: | ||
| 307 | self.memtotal = int(m.group('sz')) | ||
| 308 | break | ||
| 309 | fp.close() | ||
| 310 | def initTestOutput(self, name): | ||
| 265 | self.prefix = self.hostname | 311 | self.prefix = self.hostname |
| 266 | v = open('/proc/version', 'r').read().strip() | 312 | v = open('/proc/version', 'r').read().strip() |
| 267 | kver = string.split(v)[2] | 313 | kver = string.split(v)[2] |
| 268 | n = datetime.now() | 314 | fmt = name+'-%m%d%y-%H%M%S' |
| 269 | testtime = n.strftime('suspend-%m%d%y-%H%M%S') | 315 | testtime = datetime.now().strftime(fmt) |
| 270 | if not testpath: | ||
| 271 | testpath = n.strftime('suspend-%y%m%d-%H%M%S') | ||
| 272 | if(subdir != "."): | ||
| 273 | self.testdir = subdir+"/"+testpath | ||
| 274 | else: | ||
| 275 | self.testdir = testpath | ||
| 276 | self.teststamp = \ | 316 | self.teststamp = \ |
| 277 | '# '+testtime+' '+self.prefix+' '+self.suspendmode+' '+kver | 317 | '# '+testtime+' '+self.prefix+' '+self.suspendmode+' '+kver |
| 278 | if(self.embedded): | 318 | if(self.embedded): |
| @@ -355,7 +395,7 @@ class SystemValues: | |||
| 355 | continue | 395 | continue |
| 356 | self.tracefuncs[i] = dict() | 396 | self.tracefuncs[i] = dict() |
| 357 | def getFtraceFilterFunctions(self, current): | 397 | def getFtraceFilterFunctions(self, current): |
| 358 | rootCheck(True) | 398 | self.rootCheck(True) |
| 359 | if not current: | 399 | if not current: |
| 360 | call('cat '+self.tpath+'available_filter_functions', shell=True) | 400 | call('cat '+self.tpath+'available_filter_functions', shell=True) |
| 361 | return | 401 | return |
| @@ -453,7 +493,7 @@ class SystemValues: | |||
| 453 | val += '\nr:%s_ret %s $retval\n' % (name, func) | 493 | val += '\nr:%s_ret %s $retval\n' % (name, func) |
| 454 | return val | 494 | return val |
| 455 | def addKprobes(self, output=False): | 495 | def addKprobes(self, output=False): |
| 456 | if len(sysvals.kprobes) < 1: | 496 | if len(self.kprobes) < 1: |
| 457 | return | 497 | return |
| 458 | if output: | 498 | if output: |
| 459 | print(' kprobe functions in this kernel:') | 499 | print(' kprobe functions in this kernel:') |
| @@ -525,7 +565,7 @@ class SystemValues: | |||
| 525 | fp.flush() | 565 | fp.flush() |
| 526 | fp.close() | 566 | fp.close() |
| 527 | except: | 567 | except: |
| 528 | pass | 568 | return False |
| 529 | return True | 569 | return True |
| 530 | def fgetVal(self, path): | 570 | def fgetVal(self, path): |
| 531 | file = self.tpath+path | 571 | file = self.tpath+path |
| @@ -566,9 +606,15 @@ class SystemValues: | |||
| 566 | self.cleanupFtrace() | 606 | self.cleanupFtrace() |
| 567 | # set the trace clock to global | 607 | # set the trace clock to global |
| 568 | self.fsetVal('global', 'trace_clock') | 608 | self.fsetVal('global', 'trace_clock') |
| 569 | # set trace buffer to a huge value | ||
| 570 | self.fsetVal('nop', 'current_tracer') | 609 | self.fsetVal('nop', 'current_tracer') |
| 571 | self.fsetVal('131073', 'buffer_size_kb') | 610 | # set trace buffer to a huge value |
| 611 | if self.usecallgraph or self.usedevsrc: | ||
| 612 | tgtsize = min(self.memtotal / 2, 2*1024*1024) | ||
| 613 | maxbuf = '%d' % (tgtsize / max(1, self.cpucount)) | ||
| 614 | if self.cpucount < 1 or not self.fsetVal(maxbuf, 'buffer_size_kb'): | ||
| 615 | self.fsetVal('131072', 'buffer_size_kb') | ||
| 616 | else: | ||
| 617 | self.fsetVal('16384', 'buffer_size_kb') | ||
| 572 | # go no further if this is just a status check | 618 | # go no further if this is just a status check |
| 573 | if testing: | 619 | if testing: |
| 574 | return | 620 | return |
| @@ -641,6 +687,15 @@ class SystemValues: | |||
| 641 | if not self.ansi: | 687 | if not self.ansi: |
| 642 | return str | 688 | return str |
| 643 | return '\x1B[%d;40m%s\x1B[m' % (color, str) | 689 | return '\x1B[%d;40m%s\x1B[m' % (color, str) |
| 690 | def writeDatafileHeader(self, filename, fwdata=[]): | ||
| 691 | fp = open(filename, 'w') | ||
| 692 | fp.write(self.teststamp+'\n') | ||
| 693 | fp.write(self.sysstamp+'\n') | ||
| 694 | if(self.suspendmode == 'mem' or self.suspendmode == 'command'): | ||
| 695 | for fw in fwdata: | ||
| 696 | if(fw): | ||
| 697 | fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1])) | ||
| 698 | fp.close() | ||
| 644 | 699 | ||
| 645 | sysvals = SystemValues() | 700 | sysvals = SystemValues() |
| 646 | suspendmodename = { | 701 | suspendmodename = { |
| @@ -1008,6 +1063,12 @@ class Data: | |||
| 1008 | else: | 1063 | else: |
| 1009 | self.trimTime(self.tSuspended, \ | 1064 | self.trimTime(self.tSuspended, \ |
| 1010 | self.tResumed-self.tSuspended, False) | 1065 | self.tResumed-self.tSuspended, False) |
| 1066 | def getTimeValues(self): | ||
| 1067 | sktime = (self.dmesg['suspend_machine']['end'] - \ | ||
| 1068 | self.tKernSus) * 1000 | ||
| 1069 | rktime = (self.dmesg['resume_complete']['end'] - \ | ||
| 1070 | self.dmesg['resume_machine']['start']) * 1000 | ||
| 1071 | return (sktime, rktime) | ||
| 1011 | def setPhase(self, phase, ktime, isbegin): | 1072 | def setPhase(self, phase, ktime, isbegin): |
| 1012 | if(isbegin): | 1073 | if(isbegin): |
| 1013 | self.dmesg[phase]['start'] = ktime | 1074 | self.dmesg[phase]['start'] = ktime |
| @@ -1517,7 +1578,7 @@ class FTraceCallGraph: | |||
| 1517 | prelinedep += 1 | 1578 | prelinedep += 1 |
| 1518 | last = 0 | 1579 | last = 0 |
| 1519 | lasttime = line.time | 1580 | lasttime = line.time |
| 1520 | virtualfname = 'execution_misalignment' | 1581 | virtualfname = 'missing_function_name' |
| 1521 | if len(self.list) > 0: | 1582 | if len(self.list) > 0: |
| 1522 | last = self.list[-1] | 1583 | last = self.list[-1] |
| 1523 | lasttime = last.time | 1584 | lasttime = last.time |
| @@ -1773,24 +1834,30 @@ class Timeline: | |||
| 1773 | html_device = '<div id="{0}" title="{1}" class="thread{7}" style="left:{2}%;top:{3}px;height:{4}px;width:{5}%;{8}">{6}</div>\n' | 1834 | html_device = '<div id="{0}" title="{1}" class="thread{7}" style="left:{2}%;top:{3}px;height:{4}px;width:{5}%;{8}">{6}</div>\n' |
| 1774 | html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background:{4}">{5}</div>\n' | 1835 | html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background:{4}">{5}</div>\n' |
| 1775 | html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background:{3}"></div>\n' | 1836 | html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background:{3}"></div>\n' |
| 1837 | html_legend = '<div id="p{3}" class="square" style="left:{0}%;background:{1}"> {2}</div>\n' | ||
| 1776 | def __init__(self, rowheight, scaleheight): | 1838 | def __init__(self, rowheight, scaleheight): |
| 1777 | self.rowH = rowheight | 1839 | self.rowH = rowheight |
| 1778 | self.scaleH = scaleheight | 1840 | self.scaleH = scaleheight |
| 1779 | self.html = '' | 1841 | self.html = '' |
| 1780 | def createHeader(self, sv, suppress=''): | 1842 | def createHeader(self, sv): |
| 1781 | if(not sv.stamp['time']): | 1843 | if(not sv.stamp['time']): |
| 1782 | return | 1844 | return |
| 1783 | self.html += '<div class="version"><a href="https://01.org/suspendresume">%s v%s</a></div>' \ | 1845 | self.html += '<div class="version"><a href="https://01.org/suspendresume">%s v%s</a></div>' \ |
| 1784 | % (sv.title, sv.version) | 1846 | % (sv.title, sv.version) |
| 1785 | if sv.logmsg and 'log' not in suppress: | 1847 | if sv.logmsg and sv.testlog: |
| 1786 | self.html += '<button id="showtest" class="logbtn">log</button>' | 1848 | self.html += '<button id="showtest" class="logbtn btnfmt">log</button>' |
| 1787 | if sv.addlogs and 'dmesg' not in suppress: | 1849 | if sv.dmesglog: |
| 1788 | self.html += '<button id="showdmesg" class="logbtn">dmesg</button>' | 1850 | self.html += '<button id="showdmesg" class="logbtn btnfmt">dmesg</button>' |
| 1789 | if sv.addlogs and sv.ftracefile and 'ftrace' not in suppress: | 1851 | if sv.ftracelog: |
| 1790 | self.html += '<button id="showftrace" class="logbtn">ftrace</button>' | 1852 | self.html += '<button id="showftrace" class="logbtn btnfmt">ftrace</button>' |
| 1791 | headline_stamp = '<div class="stamp">{0} {1} {2} {3}</div>\n' | 1853 | headline_stamp = '<div class="stamp">{0} {1} {2} {3}</div>\n' |
| 1792 | self.html += headline_stamp.format(sv.stamp['host'], sv.stamp['kernel'], | 1854 | self.html += headline_stamp.format(sv.stamp['host'], sv.stamp['kernel'], |
| 1793 | sv.stamp['mode'], sv.stamp['time']) | 1855 | sv.stamp['mode'], sv.stamp['time']) |
| 1856 | if 'man' in sv.stamp and 'plat' in sv.stamp and 'cpu' in sv.stamp: | ||
| 1857 | headline_sysinfo = '<div class="stamp sysinfo">{0} {1} <i>with</i> {2}</div>\n' | ||
| 1858 | self.html += headline_sysinfo.format(sv.stamp['man'], | ||
| 1859 | sv.stamp['plat'], sv.stamp['cpu']) | ||
| 1860 | |||
| 1794 | # Function: getDeviceRows | 1861 | # Function: getDeviceRows |
| 1795 | # Description: | 1862 | # Description: |
| 1796 | # determine how may rows the device funcs will take | 1863 | # determine how may rows the device funcs will take |
| @@ -1839,7 +1906,7 @@ class Timeline: | |||
| 1839 | # devlist: the list of devices/actions in a group of contiguous phases | 1906 | # devlist: the list of devices/actions in a group of contiguous phases |
| 1840 | # Output: | 1907 | # Output: |
| 1841 | # The total number of rows needed to display this phase of the timeline | 1908 | # The total number of rows needed to display this phase of the timeline |
| 1842 | def getPhaseRows(self, devlist, row=0): | 1909 | def getPhaseRows(self, devlist, row=0, sortby='length'): |
| 1843 | # clear all rows and set them to undefined | 1910 | # clear all rows and set them to undefined |
| 1844 | remaining = len(devlist) | 1911 | remaining = len(devlist) |
| 1845 | rowdata = dict() | 1912 | rowdata = dict() |
| @@ -1852,8 +1919,12 @@ class Timeline: | |||
| 1852 | if tp not in myphases: | 1919 | if tp not in myphases: |
| 1853 | myphases.append(tp) | 1920 | myphases.append(tp) |
| 1854 | dev['row'] = -1 | 1921 | dev['row'] = -1 |
| 1855 | # sort by length 1st, then name 2nd | 1922 | if sortby == 'start': |
| 1856 | sortdict[item] = (float(dev['end']) - float(dev['start']), item.dev['name']) | 1923 | # sort by start 1st, then length 2nd |
| 1924 | sortdict[item] = (-1*float(dev['start']), float(dev['end']) - float(dev['start'])) | ||
| 1925 | else: | ||
| 1926 | # sort by length 1st, then name 2nd | ||
| 1927 | sortdict[item] = (float(dev['end']) - float(dev['start']), item.dev['name']) | ||
| 1857 | if 'src' in dev: | 1928 | if 'src' in dev: |
| 1858 | dev['devrows'] = self.getDeviceRows(dev['src']) | 1929 | dev['devrows'] = self.getDeviceRows(dev['src']) |
| 1859 | # sort the devlist by length so that large items graph on top | 1930 | # sort the devlist by length so that large items graph on top |
| @@ -1995,8 +2066,13 @@ class Timeline: | |||
| 1995 | # A list of values describing the properties of these test runs | 2066 | # A list of values describing the properties of these test runs |
| 1996 | class TestProps: | 2067 | class TestProps: |
| 1997 | stamp = '' | 2068 | stamp = '' |
| 2069 | sysinfo = '' | ||
| 1998 | S0i3 = False | 2070 | S0i3 = False |
| 1999 | fwdata = [] | 2071 | fwdata = [] |
| 2072 | stampfmt = '# [a-z]*-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\ | ||
| 2073 | '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\ | ||
| 2074 | ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$' | ||
| 2075 | sysinfofmt = '^# sysinfo .*' | ||
| 2000 | ftrace_line_fmt_fg = \ | 2076 | ftrace_line_fmt_fg = \ |
| 2001 | '^ *(?P<time>[0-9\.]*) *\| *(?P<cpu>[0-9]*)\)'+\ | 2077 | '^ *(?P<time>[0-9\.]*) *\| *(?P<cpu>[0-9]*)\)'+\ |
| 2002 | ' *(?P<proc>.*)-(?P<pid>[0-9]*) *\|'+\ | 2078 | ' *(?P<proc>.*)-(?P<pid>[0-9]*) *\|'+\ |
| @@ -2019,6 +2095,36 @@ class TestProps: | |||
| 2019 | self.ftrace_line_fmt = self.ftrace_line_fmt_nop | 2095 | self.ftrace_line_fmt = self.ftrace_line_fmt_nop |
| 2020 | else: | 2096 | else: |
| 2021 | doError('Invalid tracer format: [%s]' % tracer) | 2097 | doError('Invalid tracer format: [%s]' % tracer) |
| 2098 | def parseStamp(self, data, sv): | ||
| 2099 | m = re.match(self.stampfmt, self.stamp) | ||
| 2100 | data.stamp = {'time': '', 'host': '', 'mode': ''} | ||
| 2101 | dt = datetime(int(m.group('y'))+2000, int(m.group('m')), | ||
| 2102 | int(m.group('d')), int(m.group('H')), int(m.group('M')), | ||
| 2103 | int(m.group('S'))) | ||
| 2104 | data.stamp['time'] = dt.strftime('%B %d %Y, %I:%M:%S %p') | ||
| 2105 | data.stamp['host'] = m.group('host') | ||
| 2106 | data.stamp['mode'] = m.group('mode') | ||
| 2107 | data.stamp['kernel'] = m.group('kernel') | ||
| 2108 | if re.match(self.sysinfofmt, self.sysinfo): | ||
| 2109 | for f in self.sysinfo.split('|'): | ||
| 2110 | if '#' in f: | ||
| 2111 | continue | ||
| 2112 | tmp = f.strip().split(':', 1) | ||
| 2113 | key = tmp[0] | ||
| 2114 | val = tmp[1] | ||
| 2115 | data.stamp[key] = val | ||
| 2116 | sv.hostname = data.stamp['host'] | ||
| 2117 | sv.suspendmode = data.stamp['mode'] | ||
| 2118 | if sv.suspendmode == 'command' and sv.ftracefile != '': | ||
| 2119 | modes = ['on', 'freeze', 'standby', 'mem'] | ||
| 2120 | out = Popen(['grep', 'suspend_enter', sv.ftracefile], | ||
| 2121 | stderr=PIPE, stdout=PIPE).stdout.read() | ||
| 2122 | m = re.match('.* suspend_enter\[(?P<mode>.*)\]', out) | ||
| 2123 | if m and m.group('mode') in ['1', '2', '3']: | ||
| 2124 | sv.suspendmode = modes[int(m.group('mode'))] | ||
| 2125 | data.stamp['mode'] = sv.suspendmode | ||
| 2126 | if not sv.stamp: | ||
| 2127 | sv.stamp = data.stamp | ||
| 2022 | 2128 | ||
| 2023 | # Class: TestRun | 2129 | # Class: TestRun |
| 2024 | # Description: | 2130 | # Description: |
| @@ -2090,35 +2196,6 @@ def vprint(msg): | |||
| 2090 | if(sysvals.verbose): | 2196 | if(sysvals.verbose): |
| 2091 | print(msg) | 2197 | print(msg) |
| 2092 | 2198 | ||
| 2093 | # Function: parseStamp | ||
| 2094 | # Description: | ||
| 2095 | # Pull in the stamp comment line from the data file(s), | ||
| 2096 | # create the stamp, and add it to the global sysvals object | ||
| 2097 | # Arguments: | ||
| 2098 | # m: the valid re.match output for the stamp line | ||
| 2099 | def parseStamp(line, data): | ||
| 2100 | m = re.match(sysvals.stampfmt, line) | ||
| 2101 | data.stamp = {'time': '', 'host': '', 'mode': ''} | ||
| 2102 | dt = datetime(int(m.group('y'))+2000, int(m.group('m')), | ||
| 2103 | int(m.group('d')), int(m.group('H')), int(m.group('M')), | ||
| 2104 | int(m.group('S'))) | ||
| 2105 | data.stamp['time'] = dt.strftime('%B %d %Y, %I:%M:%S %p') | ||
| 2106 | data.stamp['host'] = m.group('host') | ||
| 2107 | data.stamp['mode'] = m.group('mode') | ||
| 2108 | data.stamp['kernel'] = m.group('kernel') | ||
| 2109 | sysvals.hostname = data.stamp['host'] | ||
| 2110 | sysvals.suspendmode = data.stamp['mode'] | ||
| 2111 | if sysvals.suspendmode == 'command' and sysvals.ftracefile != '': | ||
| 2112 | modes = ['on', 'freeze', 'standby', 'mem'] | ||
| 2113 | out = Popen(['grep', 'suspend_enter', sysvals.ftracefile], | ||
| 2114 | stderr=PIPE, stdout=PIPE).stdout.read() | ||
| 2115 | m = re.match('.* suspend_enter\[(?P<mode>.*)\]', out) | ||
| 2116 | if m and m.group('mode') in ['1', '2', '3']: | ||
| 2117 | sysvals.suspendmode = modes[int(m.group('mode'))] | ||
| 2118 | data.stamp['mode'] = sysvals.suspendmode | ||
| 2119 | if not sysvals.stamp: | ||
| 2120 | sysvals.stamp = data.stamp | ||
| 2121 | |||
| 2122 | # Function: doesTraceLogHaveTraceEvents | 2199 | # Function: doesTraceLogHaveTraceEvents |
| 2123 | # Description: | 2200 | # Description: |
| 2124 | # Quickly determine if the ftrace log has some or all of the trace events | 2201 | # Quickly determine if the ftrace log has some or all of the trace events |
| @@ -2136,11 +2213,6 @@ def doesTraceLogHaveTraceEvents(): | |||
| 2136 | sysvals.usekprobes = True | 2213 | sysvals.usekprobes = True |
| 2137 | out = Popen(['head', '-1', sysvals.ftracefile], | 2214 | out = Popen(['head', '-1', sysvals.ftracefile], |
| 2138 | stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '') | 2215 | stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '') |
| 2139 | m = re.match(sysvals.stampfmt, out) | ||
| 2140 | if m and m.group('mode') == 'command': | ||
| 2141 | sysvals.usetraceeventsonly = True | ||
| 2142 | sysvals.usetraceevents = True | ||
| 2143 | return | ||
| 2144 | # figure out what level of trace events are supported | 2216 | # figure out what level of trace events are supported |
| 2145 | sysvals.usetraceeventsonly = True | 2217 | sysvals.usetraceeventsonly = True |
| 2146 | sysvals.usetraceevents = False | 2218 | sysvals.usetraceevents = False |
| @@ -2182,11 +2254,13 @@ def appendIncompleteTraceLog(testruns): | |||
| 2182 | for line in tf: | 2254 | for line in tf: |
| 2183 | # remove any latent carriage returns | 2255 | # remove any latent carriage returns |
| 2184 | line = line.replace('\r\n', '') | 2256 | line = line.replace('\r\n', '') |
| 2185 | # grab the time stamp | 2257 | # grab the stamp and sysinfo |
| 2186 | m = re.match(sysvals.stampfmt, line) | 2258 | if re.match(tp.stampfmt, line): |
| 2187 | if(m): | ||
| 2188 | tp.stamp = line | 2259 | tp.stamp = line |
| 2189 | continue | 2260 | continue |
| 2261 | elif re.match(tp.sysinfofmt, line): | ||
| 2262 | tp.sysinfo = line | ||
| 2263 | continue | ||
| 2190 | # determine the trace data type (required for further parsing) | 2264 | # determine the trace data type (required for further parsing) |
| 2191 | m = re.match(sysvals.tracertypefmt, line) | 2265 | m = re.match(sysvals.tracertypefmt, line) |
| 2192 | if(m): | 2266 | if(m): |
| @@ -2219,7 +2293,7 @@ def appendIncompleteTraceLog(testruns): | |||
| 2219 | # look for the suspend start marker | 2293 | # look for the suspend start marker |
| 2220 | if(t.startMarker()): | 2294 | if(t.startMarker()): |
| 2221 | data = testrun[testidx].data | 2295 | data = testrun[testidx].data |
| 2222 | parseStamp(tp.stamp, data) | 2296 | tp.parseStamp(data, sysvals) |
| 2223 | data.setStart(t.time) | 2297 | data.setStart(t.time) |
| 2224 | continue | 2298 | continue |
| 2225 | if(not data): | 2299 | if(not data): |
| @@ -2389,11 +2463,13 @@ def parseTraceLog(): | |||
| 2389 | for line in tf: | 2463 | for line in tf: |
| 2390 | # remove any latent carriage returns | 2464 | # remove any latent carriage returns |
| 2391 | line = line.replace('\r\n', '') | 2465 | line = line.replace('\r\n', '') |
| 2392 | # stamp line: each stamp means a new test run | 2466 | # stamp and sysinfo lines |
| 2393 | m = re.match(sysvals.stampfmt, line) | 2467 | if re.match(tp.stampfmt, line): |
| 2394 | if(m): | ||
| 2395 | tp.stamp = line | 2468 | tp.stamp = line |
| 2396 | continue | 2469 | continue |
| 2470 | elif re.match(tp.sysinfofmt, line): | ||
| 2471 | tp.sysinfo = line | ||
| 2472 | continue | ||
| 2397 | # firmware line: pull out any firmware data | 2473 | # firmware line: pull out any firmware data |
| 2398 | m = re.match(sysvals.firmwarefmt, line) | 2474 | m = re.match(sysvals.firmwarefmt, line) |
| 2399 | if(m): | 2475 | if(m): |
| @@ -2439,7 +2515,7 @@ def parseTraceLog(): | |||
| 2439 | testdata.append(data) | 2515 | testdata.append(data) |
| 2440 | testrun = TestRun(data) | 2516 | testrun = TestRun(data) |
| 2441 | testruns.append(testrun) | 2517 | testruns.append(testrun) |
| 2442 | parseStamp(tp.stamp, data) | 2518 | tp.parseStamp(data, sysvals) |
| 2443 | data.setStart(t.time) | 2519 | data.setStart(t.time) |
| 2444 | data.tKernSus = t.time | 2520 | data.tKernSus = t.time |
| 2445 | continue | 2521 | continue |
| @@ -2820,10 +2896,13 @@ def loadKernelLog(justtext=False): | |||
| 2820 | idx = line.find('[') | 2896 | idx = line.find('[') |
| 2821 | if idx > 1: | 2897 | if idx > 1: |
| 2822 | line = line[idx:] | 2898 | line = line[idx:] |
| 2823 | m = re.match(sysvals.stampfmt, line) | 2899 | # grab the stamp and sysinfo |
| 2824 | if(m): | 2900 | if re.match(tp.stampfmt, line): |
| 2825 | tp.stamp = line | 2901 | tp.stamp = line |
| 2826 | continue | 2902 | continue |
| 2903 | elif re.match(tp.sysinfofmt, line): | ||
| 2904 | tp.sysinfo = line | ||
| 2905 | continue | ||
| 2827 | m = re.match(sysvals.firmwarefmt, line) | 2906 | m = re.match(sysvals.firmwarefmt, line) |
| 2828 | if(m): | 2907 | if(m): |
| 2829 | tp.fwdata.append((int(m.group('s')), int(m.group('r')))) | 2908 | tp.fwdata.append((int(m.group('s')), int(m.group('r')))) |
| @@ -2839,7 +2918,7 @@ def loadKernelLog(justtext=False): | |||
| 2839 | if(data): | 2918 | if(data): |
| 2840 | testruns.append(data) | 2919 | testruns.append(data) |
| 2841 | data = Data(len(testruns)) | 2920 | data = Data(len(testruns)) |
| 2842 | parseStamp(tp.stamp, data) | 2921 | tp.parseStamp(data, sysvals) |
| 2843 | if len(tp.fwdata) > data.testnumber: | 2922 | if len(tp.fwdata) > data.testnumber: |
| 2844 | data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber] | 2923 | data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber] |
| 2845 | if(data.fwSuspend > 0 or data.fwResume > 0): | 2924 | if(data.fwSuspend > 0 or data.fwResume > 0): |
| @@ -3170,6 +3249,8 @@ def addCallgraphs(sv, hf, data): | |||
| 3170 | continue | 3249 | continue |
| 3171 | list = data.dmesg[p]['list'] | 3250 | list = data.dmesg[p]['list'] |
| 3172 | for devname in data.sortedDevices(p): | 3251 | for devname in data.sortedDevices(p): |
| 3252 | if len(sv.devicefilter) > 0 and devname not in sv.devicefilter: | ||
| 3253 | continue | ||
| 3173 | dev = list[devname] | 3254 | dev = list[devname] |
| 3174 | color = 'white' | 3255 | color = 'white' |
| 3175 | if 'color' in data.dmesg[p]: | 3256 | if 'color' in data.dmesg[p]: |
| @@ -3309,7 +3390,6 @@ def createHTML(testruns): | |||
| 3309 | html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">ERROR→</div>\n' | 3390 | html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">ERROR→</div>\n' |
| 3310 | html_traceevent = '<div title="{0}" class="traceevent{6}" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;{7}">{5}</div>\n' | 3391 | html_traceevent = '<div title="{0}" class="traceevent{6}" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;{7}">{5}</div>\n' |
| 3311 | html_cpuexec = '<div class="jiffie" style="left:{0}%;top:{1}px;height:{2}px;width:{3}%;background:{4};"></div>\n' | 3392 | html_cpuexec = '<div class="jiffie" style="left:{0}%;top:{1}px;height:{2}px;width:{3}%;background:{4};"></div>\n' |
| 3312 | html_legend = '<div id="p{3}" class="square" style="left:{0}%;background:{1}"> {2}</div>\n' | ||
| 3313 | html_timetotal = '<table class="time1">\n<tr>'\ | 3393 | html_timetotal = '<table class="time1">\n<tr>'\ |
| 3314 | '<td class="green" title="{3}">{2} Suspend Time: <b>{0} ms</b></td>'\ | 3394 | '<td class="green" title="{3}">{2} Suspend Time: <b>{0} ms</b></td>'\ |
| 3315 | '<td class="yellow" title="{4}">{2} Resume Time: <b>{1} ms</b></td>'\ | 3395 | '<td class="yellow" title="{4}">{2} Resume Time: <b>{1} ms</b></td>'\ |
| @@ -3346,10 +3426,7 @@ def createHTML(testruns): | |||
| 3346 | # Generate the header for this timeline | 3426 | # Generate the header for this timeline |
| 3347 | for data in testruns: | 3427 | for data in testruns: |
| 3348 | tTotal = data.end - data.start | 3428 | tTotal = data.end - data.start |
| 3349 | sktime = (data.dmesg['suspend_machine']['end'] - \ | 3429 | sktime, rktime = data.getTimeValues() |
| 3350 | data.tKernSus) * 1000 | ||
| 3351 | rktime = (data.dmesg['resume_complete']['end'] - \ | ||
| 3352 | data.dmesg['resume_machine']['start']) * 1000 | ||
| 3353 | if(tTotal == 0): | 3430 | if(tTotal == 0): |
| 3354 | print('ERROR: No timeline data') | 3431 | print('ERROR: No timeline data') |
| 3355 | sys.exit() | 3432 | sys.exit() |
| @@ -3581,7 +3658,7 @@ def createHTML(testruns): | |||
| 3581 | id += tmp[1][0] | 3658 | id += tmp[1][0] |
| 3582 | order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin) | 3659 | order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin) |
| 3583 | name = string.replace(phase, '_', ' ') | 3660 | name = string.replace(phase, '_', ' ') |
| 3584 | devtl.html += html_legend.format(order, \ | 3661 | devtl.html += devtl.html_legend.format(order, \ |
| 3585 | data.dmesg[phase]['color'], name, id) | 3662 | data.dmesg[phase]['color'], name, id) |
| 3586 | devtl.html += '</div>\n' | 3663 | devtl.html += '</div>\n' |
| 3587 | 3664 | ||
| @@ -3628,10 +3705,10 @@ def createHTML(testruns): | |||
| 3628 | addCallgraphs(sysvals, hf, data) | 3705 | addCallgraphs(sysvals, hf, data) |
| 3629 | 3706 | ||
| 3630 | # add the test log as a hidden div | 3707 | # add the test log as a hidden div |
| 3631 | if sysvals.logmsg: | 3708 | if sysvals.testlog and sysvals.logmsg: |
| 3632 | hf.write('<div id="testlog" style="display:none;">\n'+sysvals.logmsg+'</div>\n') | 3709 | hf.write('<div id="testlog" style="display:none;">\n'+sysvals.logmsg+'</div>\n') |
| 3633 | # add the dmesg log as a hidden div | 3710 | # add the dmesg log as a hidden div |
| 3634 | if sysvals.addlogs and sysvals.dmesgfile: | 3711 | if sysvals.dmesglog and sysvals.dmesgfile: |
| 3635 | hf.write('<div id="dmesglog" style="display:none;">\n') | 3712 | hf.write('<div id="dmesglog" style="display:none;">\n') |
| 3636 | lf = open(sysvals.dmesgfile, 'r') | 3713 | lf = open(sysvals.dmesgfile, 'r') |
| 3637 | for line in lf: | 3714 | for line in lf: |
| @@ -3640,7 +3717,7 @@ def createHTML(testruns): | |||
| 3640 | lf.close() | 3717 | lf.close() |
| 3641 | hf.write('</div>\n') | 3718 | hf.write('</div>\n') |
| 3642 | # add the ftrace log as a hidden div | 3719 | # add the ftrace log as a hidden div |
| 3643 | if sysvals.addlogs and sysvals.ftracefile: | 3720 | if sysvals.ftracelog and sysvals.ftracefile: |
| 3644 | hf.write('<div id="ftracelog" style="display:none;">\n') | 3721 | hf.write('<div id="ftracelog" style="display:none;">\n') |
| 3645 | lf = open(sysvals.ftracefile, 'r') | 3722 | lf = open(sysvals.ftracefile, 'r') |
| 3646 | for line in lf: | 3723 | for line in lf: |
| @@ -3701,6 +3778,7 @@ def addCSS(hf, sv, testcount=1, kerror=False, extra=''): | |||
| 3701 | <style type=\'text/css\'>\n\ | 3778 | <style type=\'text/css\'>\n\ |
| 3702 | body {overflow-y:scroll;}\n\ | 3779 | body {overflow-y:scroll;}\n\ |
| 3703 | .stamp {width:100%;text-align:center;background:gray;line-height:30px;color:white;font:25px Arial;}\n\ | 3780 | .stamp {width:100%;text-align:center;background:gray;line-height:30px;color:white;font:25px Arial;}\n\ |
| 3781 | .stamp.sysinfo {font:10px Arial;}\n\ | ||
| 3704 | .callgraph {margin-top:30px;box-shadow:5px 5px 20px black;}\n\ | 3782 | .callgraph {margin-top:30px;box-shadow:5px 5px 20px black;}\n\ |
| 3705 | .callgraph article * {padding-left:28px;}\n\ | 3783 | .callgraph article * {padding-left:28px;}\n\ |
| 3706 | h1 {color:black;font:bold 30px Times;}\n\ | 3784 | h1 {color:black;font:bold 30px Times;}\n\ |
| @@ -3746,7 +3824,7 @@ def addCSS(hf, sv, testcount=1, kerror=False, extra=''): | |||
| 3746 | .legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\ | 3824 | .legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\ |
| 3747 | .legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\ | 3825 | .legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\ |
| 3748 | button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\ | 3826 | button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\ |
| 3749 | .logbtn {position:relative;float:right;height:25px;width:50px;margin-top:3px;margin-bottom:0;font-size:10px;text-align:center;}\n\ | 3827 | .btnfmt {position:relative;float:right;height:25px;width:auto;margin-top:3px;margin-bottom:0;font-size:10px;text-align:center;}\n\ |
| 3750 | .devlist {position:'+devlistpos+';width:190px;}\n\ | 3828 | .devlist {position:'+devlistpos+';width:190px;}\n\ |
| 3751 | a:link {color:white;text-decoration:none;}\n\ | 3829 | a:link {color:white;text-decoration:none;}\n\ |
| 3752 | a:visited {color:white;}\n\ | 3830 | a:visited {color:white;}\n\ |
| @@ -4084,8 +4162,6 @@ def addScriptCode(hf, testruns): | |||
| 4084 | ' win.document.write(title+"<pre>"+log.innerHTML+"</pre>");\n'\ | 4162 | ' win.document.write(title+"<pre>"+log.innerHTML+"</pre>");\n'\ |
| 4085 | ' win.document.close();\n'\ | 4163 | ' win.document.close();\n'\ |
| 4086 | ' }\n'\ | 4164 | ' }\n'\ |
| 4087 | ' function onClickPhase(e) {\n'\ | ||
| 4088 | ' }\n'\ | ||
| 4089 | ' function onMouseDown(e) {\n'\ | 4165 | ' function onMouseDown(e) {\n'\ |
| 4090 | ' dragval[0] = e.clientX;\n'\ | 4166 | ' dragval[0] = e.clientX;\n'\ |
| 4091 | ' dragval[1] = document.getElementById("dmesgzoombox").scrollLeft;\n'\ | 4167 | ' dragval[1] = document.getElementById("dmesgzoombox").scrollLeft;\n'\ |
| @@ -4120,9 +4196,6 @@ def addScriptCode(hf, testruns): | |||
| 4120 | ' document.getElementById("zoomin").onclick = zoomTimeline;\n'\ | 4196 | ' document.getElementById("zoomin").onclick = zoomTimeline;\n'\ |
| 4121 | ' document.getElementById("zoomout").onclick = zoomTimeline;\n'\ | 4197 | ' document.getElementById("zoomout").onclick = zoomTimeline;\n'\ |
| 4122 | ' document.getElementById("zoomdef").onclick = zoomTimeline;\n'\ | 4198 | ' document.getElementById("zoomdef").onclick = zoomTimeline;\n'\ |
| 4123 | ' var list = document.getElementsByClassName("square");\n'\ | ||
| 4124 | ' for (var i = 0; i < list.length; i++)\n'\ | ||
| 4125 | ' list[i].onclick = onClickPhase;\n'\ | ||
| 4126 | ' var list = document.getElementsByClassName("err");\n'\ | 4199 | ' var list = document.getElementsByClassName("err");\n'\ |
| 4127 | ' for (var i = 0; i < list.length; i++)\n'\ | 4200 | ' for (var i = 0; i < list.length; i++)\n'\ |
| 4128 | ' list[i].onclick = errWindow;\n'\ | 4201 | ' list[i].onclick = errWindow;\n'\ |
| @@ -4193,8 +4266,14 @@ def executeSuspend(): | |||
| 4193 | if sysvals.testcommand != '': | 4266 | if sysvals.testcommand != '': |
| 4194 | call(sysvals.testcommand+' 2>&1', shell=True); | 4267 | call(sysvals.testcommand+' 2>&1', shell=True); |
| 4195 | else: | 4268 | else: |
| 4269 | mode = sysvals.suspendmode | ||
| 4270 | if sysvals.memmode and os.path.exists(sysvals.mempowerfile): | ||
| 4271 | mode = 'mem' | ||
| 4272 | pf = open(sysvals.mempowerfile, 'w') | ||
| 4273 | pf.write(sysvals.memmode) | ||
| 4274 | pf.close() | ||
| 4196 | pf = open(sysvals.powerfile, 'w') | 4275 | pf = open(sysvals.powerfile, 'w') |
| 4197 | pf.write(sysvals.suspendmode) | 4276 | pf.write(mode) |
| 4198 | # execution will pause here | 4277 | # execution will pause here |
| 4199 | try: | 4278 | try: |
| 4200 | pf.close() | 4279 | pf.close() |
| @@ -4219,24 +4298,15 @@ def executeSuspend(): | |||
| 4219 | pm.stop() | 4298 | pm.stop() |
| 4220 | sysvals.fsetVal('0', 'tracing_on') | 4299 | sysvals.fsetVal('0', 'tracing_on') |
| 4221 | print('CAPTURING TRACE') | 4300 | print('CAPTURING TRACE') |
| 4222 | writeDatafileHeader(sysvals.ftracefile, fwdata) | 4301 | sysvals.writeDatafileHeader(sysvals.ftracefile, fwdata) |
| 4223 | call('cat '+tp+'trace >> '+sysvals.ftracefile, shell=True) | 4302 | call('cat '+tp+'trace >> '+sysvals.ftracefile, shell=True) |
| 4224 | sysvals.fsetVal('', 'trace') | 4303 | sysvals.fsetVal('', 'trace') |
| 4225 | devProps() | 4304 | devProps() |
| 4226 | # grab a copy of the dmesg output | 4305 | # grab a copy of the dmesg output |
| 4227 | print('CAPTURING DMESG') | 4306 | print('CAPTURING DMESG') |
| 4228 | writeDatafileHeader(sysvals.dmesgfile, fwdata) | 4307 | sysvals.writeDatafileHeader(sysvals.dmesgfile, fwdata) |
| 4229 | sysvals.getdmesg() | 4308 | sysvals.getdmesg() |
| 4230 | 4309 | ||
| 4231 | def writeDatafileHeader(filename, fwdata): | ||
| 4232 | fp = open(filename, 'a') | ||
| 4233 | fp.write(sysvals.teststamp+'\n') | ||
| 4234 | if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'): | ||
| 4235 | for fw in fwdata: | ||
| 4236 | if(fw): | ||
| 4237 | fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1])) | ||
| 4238 | fp.close() | ||
| 4239 | |||
| 4240 | # Function: setUSBDevicesAuto | 4310 | # Function: setUSBDevicesAuto |
| 4241 | # Description: | 4311 | # Description: |
| 4242 | # Set the autosuspend control parameter of all USB devices to auto | 4312 | # Set the autosuspend control parameter of all USB devices to auto |
| @@ -4244,7 +4314,7 @@ def writeDatafileHeader(filename, fwdata): | |||
| 4244 | # to always-on since the kernel cant determine if the device can | 4314 | # to always-on since the kernel cant determine if the device can |
| 4245 | # properly autosuspend | 4315 | # properly autosuspend |
| 4246 | def setUSBDevicesAuto(): | 4316 | def setUSBDevicesAuto(): |
| 4247 | rootCheck(True) | 4317 | sysvals.rootCheck(True) |
| 4248 | for dirname, dirnames, filenames in os.walk('/sys/devices'): | 4318 | for dirname, dirnames, filenames in os.walk('/sys/devices'): |
| 4249 | if(re.match('.*/usb[0-9]*.*', dirname) and | 4319 | if(re.match('.*/usb[0-9]*.*', dirname) and |
| 4250 | 'idVendor' in filenames and 'idProduct' in filenames): | 4320 | 'idVendor' in filenames and 'idProduct' in filenames): |
| @@ -4467,13 +4537,146 @@ def devProps(data=0): | |||
| 4467 | # Output: | 4537 | # Output: |
| 4468 | # A string list of the available modes | 4538 | # A string list of the available modes |
| 4469 | def getModes(): | 4539 | def getModes(): |
| 4470 | modes = '' | 4540 | modes = [] |
| 4471 | if(os.path.exists(sysvals.powerfile)): | 4541 | if(os.path.exists(sysvals.powerfile)): |
| 4472 | fp = open(sysvals.powerfile, 'r') | 4542 | fp = open(sysvals.powerfile, 'r') |
| 4473 | modes = string.split(fp.read()) | 4543 | modes = string.split(fp.read()) |
| 4474 | fp.close() | 4544 | fp.close() |
| 4545 | if(os.path.exists(sysvals.mempowerfile)): | ||
| 4546 | deep = False | ||
| 4547 | fp = open(sysvals.mempowerfile, 'r') | ||
| 4548 | for m in string.split(fp.read()): | ||
| 4549 | memmode = m.strip('[]') | ||
| 4550 | if memmode == 'deep': | ||
| 4551 | deep = True | ||
| 4552 | else: | ||
| 4553 | modes.append('mem-%s' % memmode) | ||
| 4554 | fp.close() | ||
| 4555 | if 'mem' in modes and not deep: | ||
| 4556 | modes.remove('mem') | ||
| 4475 | return modes | 4557 | return modes |
| 4476 | 4558 | ||
| 4559 | # Function: dmidecode | ||
| 4560 | # Description: | ||
| 4561 | # Read the bios tables and pull out system info | ||
| 4562 | # Arguments: | ||
| 4563 | # mempath: /dev/mem or custom mem path | ||
| 4564 | # fatal: True to exit on error, False to return empty dict | ||
| 4565 | # Output: | ||
| 4566 | # A dict object with all available key/values | ||
| 4567 | def dmidecode(mempath, fatal=False): | ||
| 4568 | out = dict() | ||
| 4569 | |||
| 4570 | # the list of values to retrieve, with hardcoded (type, idx) | ||
| 4571 | info = { | ||
| 4572 | 'bios-vendor': (0, 4), | ||
| 4573 | 'bios-version': (0, 5), | ||
| 4574 | 'bios-release-date': (0, 8), | ||
| 4575 | 'system-manufacturer': (1, 4), | ||
| 4576 | 'system-product-name': (1, 5), | ||
| 4577 | 'system-version': (1, 6), | ||
| 4578 | 'system-serial-number': (1, 7), | ||
| 4579 | 'baseboard-manufacturer': (2, 4), | ||
| 4580 | 'baseboard-product-name': (2, 5), | ||
| 4581 | 'baseboard-version': (2, 6), | ||
| 4582 | 'baseboard-serial-number': (2, 7), | ||
| 4583 | 'chassis-manufacturer': (3, 4), | ||
| 4584 | 'chassis-type': (3, 5), | ||
| 4585 | 'chassis-version': (3, 6), | ||
| 4586 | 'chassis-serial-number': (3, 7), | ||
| 4587 | 'processor-manufacturer': (4, 7), | ||
| 4588 | 'processor-version': (4, 16), | ||
| 4589 | } | ||
| 4590 | if(not os.path.exists(mempath)): | ||
| 4591 | if(fatal): | ||
| 4592 | doError('file does not exist: %s' % mempath) | ||
| 4593 | return out | ||
| 4594 | if(not os.access(mempath, os.R_OK)): | ||
| 4595 | if(fatal): | ||
| 4596 | doError('file is not readable: %s' % mempath) | ||
| 4597 | return out | ||
| 4598 | |||
| 4599 | # by default use legacy scan, but try to use EFI first | ||
| 4600 | memaddr = 0xf0000 | ||
| 4601 | memsize = 0x10000 | ||
| 4602 | for ep in ['/sys/firmware/efi/systab', '/proc/efi/systab']: | ||
| 4603 | if not os.path.exists(ep) or not os.access(ep, os.R_OK): | ||
| 4604 | continue | ||
| 4605 | fp = open(ep, 'r') | ||
| 4606 | buf = fp.read() | ||
| 4607 | fp.close() | ||
| 4608 | i = buf.find('SMBIOS=') | ||
| 4609 | if i >= 0: | ||
| 4610 | try: | ||
| 4611 | memaddr = int(buf[i+7:], 16) | ||
| 4612 | memsize = 0x20 | ||
| 4613 | except: | ||
| 4614 | continue | ||
| 4615 | |||
| 4616 | # read in the memory for scanning | ||
| 4617 | fp = open(mempath, 'rb') | ||
| 4618 | try: | ||
| 4619 | fp.seek(memaddr) | ||
| 4620 | buf = fp.read(memsize) | ||
| 4621 | except: | ||
| 4622 | if(fatal): | ||
| 4623 | doError('DMI table is unreachable, sorry') | ||
| 4624 | else: | ||
| 4625 | return out | ||
| 4626 | fp.close() | ||
| 4627 | |||
| 4628 | # search for either an SM table or DMI table | ||
| 4629 | i = base = length = num = 0 | ||
| 4630 | while(i < memsize): | ||
| 4631 | if buf[i:i+4] == '_SM_' and i < memsize - 16: | ||
| 4632 | length = struct.unpack('H', buf[i+22:i+24])[0] | ||
| 4633 | base, num = struct.unpack('IH', buf[i+24:i+30]) | ||
| 4634 | break | ||
| 4635 | elif buf[i:i+5] == '_DMI_': | ||
| 4636 | length = struct.unpack('H', buf[i+6:i+8])[0] | ||
| 4637 | base, num = struct.unpack('IH', buf[i+8:i+14]) | ||
| 4638 | break | ||
| 4639 | i += 16 | ||
| 4640 | if base == 0 and length == 0 and num == 0: | ||
| 4641 | if(fatal): | ||
| 4642 | doError('Neither SMBIOS nor DMI were found') | ||
| 4643 | else: | ||
| 4644 | return out | ||
| 4645 | |||
| 4646 | # read in the SM or DMI table | ||
| 4647 | fp = open(mempath, 'rb') | ||
| 4648 | try: | ||
| 4649 | fp.seek(base) | ||
| 4650 | buf = fp.read(length) | ||
| 4651 | except: | ||
| 4652 | if(fatal): | ||
| 4653 | doError('DMI table is unreachable, sorry') | ||
| 4654 | else: | ||
| 4655 | return out | ||
| 4656 | fp.close() | ||
| 4657 | |||
| 4658 | # scan the table for the values we want | ||
| 4659 | count = i = 0 | ||
| 4660 | while(count < num and i <= len(buf) - 4): | ||
| 4661 | type, size, handle = struct.unpack('BBH', buf[i:i+4]) | ||
| 4662 | n = i + size | ||
| 4663 | while n < len(buf) - 1: | ||
| 4664 | if 0 == struct.unpack('H', buf[n:n+2])[0]: | ||
| 4665 | break | ||
| 4666 | n += 1 | ||
| 4667 | data = buf[i+size:n+2].split('\0') | ||
| 4668 | for name in info: | ||
| 4669 | itype, idxadr = info[name] | ||
| 4670 | if itype == type: | ||
| 4671 | idx = struct.unpack('B', buf[i+idxadr])[0] | ||
| 4672 | if idx > 0 and idx < len(data) - 1: | ||
| 4673 | s = data[idx-1].strip() | ||
| 4674 | if s and s.lower() != 'to be filled by o.e.m.': | ||
| 4675 | out[name] = data[idx-1] | ||
| 4676 | i = n + 2 | ||
| 4677 | count += 1 | ||
| 4678 | return out | ||
| 4679 | |||
| 4477 | # Function: getFPDT | 4680 | # Function: getFPDT |
| 4478 | # Description: | 4681 | # Description: |
| 4479 | # Read the acpi bios tables and pull out FPDT, the firmware data | 4682 | # Read the acpi bios tables and pull out FPDT, the firmware data |
| @@ -4487,7 +4690,7 @@ def getFPDT(output): | |||
| 4487 | prectype[0] = 'Basic S3 Resume Performance Record' | 4690 | prectype[0] = 'Basic S3 Resume Performance Record' |
| 4488 | prectype[1] = 'Basic S3 Suspend Performance Record' | 4691 | prectype[1] = 'Basic S3 Suspend Performance Record' |
| 4489 | 4692 | ||
| 4490 | rootCheck(True) | 4693 | sysvals.rootCheck(True) |
| 4491 | if(not os.path.exists(sysvals.fpdtpath)): | 4694 | if(not os.path.exists(sysvals.fpdtpath)): |
| 4492 | if(output): | 4695 | if(output): |
| 4493 | doError('file does not exist: %s' % sysvals.fpdtpath) | 4696 | doError('file does not exist: %s' % sysvals.fpdtpath) |
| @@ -4617,7 +4820,7 @@ def statusCheck(probecheck=False): | |||
| 4617 | 4820 | ||
| 4618 | # check we have root access | 4821 | # check we have root access |
| 4619 | res = sysvals.colorText('NO (No features of this tool will work!)') | 4822 | res = sysvals.colorText('NO (No features of this tool will work!)') |
| 4620 | if(rootCheck(False)): | 4823 | if(sysvals.rootCheck(False)): |
| 4621 | res = 'YES' | 4824 | res = 'YES' |
| 4622 | print(' have root access: %s' % res) | 4825 | print(' have root access: %s' % res) |
| 4623 | if(res != 'YES'): | 4826 | if(res != 'YES'): |
| @@ -4716,16 +4919,6 @@ def doError(msg, help=False): | |||
| 4716 | print('ERROR: %s\n') % msg | 4919 | print('ERROR: %s\n') % msg |
| 4717 | sys.exit() | 4920 | sys.exit() |
| 4718 | 4921 | ||
| 4719 | # Function: rootCheck | ||
| 4720 | # Description: | ||
| 4721 | # quick check to see if we have root access | ||
| 4722 | def rootCheck(fatal): | ||
| 4723 | if(os.access(sysvals.powerfile, os.W_OK)): | ||
| 4724 | return True | ||
| 4725 | if fatal: | ||
| 4726 | doError('This command requires sysfs mount and root access') | ||
| 4727 | return False | ||
| 4728 | |||
| 4729 | # Function: getArgInt | 4922 | # Function: getArgInt |
| 4730 | # Description: | 4923 | # Description: |
| 4731 | # pull out an integer argument from the command line with checks | 4924 | # pull out an integer argument from the command line with checks |
| @@ -4779,6 +4972,7 @@ def processData(): | |||
| 4779 | if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)): | 4972 | if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)): |
| 4780 | appendIncompleteTraceLog(testruns) | 4973 | appendIncompleteTraceLog(testruns) |
| 4781 | createHTML(testruns) | 4974 | createHTML(testruns) |
| 4975 | return testruns | ||
| 4782 | 4976 | ||
| 4783 | # Function: rerunTest | 4977 | # Function: rerunTest |
| 4784 | # Description: | 4978 | # Description: |
| @@ -4790,17 +4984,20 @@ def rerunTest(): | |||
| 4790 | doError('recreating this html output requires a dmesg file') | 4984 | doError('recreating this html output requires a dmesg file') |
| 4791 | sysvals.setOutputFile() | 4985 | sysvals.setOutputFile() |
| 4792 | vprint('Output file: %s' % sysvals.htmlfile) | 4986 | vprint('Output file: %s' % sysvals.htmlfile) |
| 4793 | if(os.path.exists(sysvals.htmlfile) and not os.access(sysvals.htmlfile, os.W_OK)): | 4987 | if os.path.exists(sysvals.htmlfile): |
| 4794 | doError('missing permission to write to %s' % sysvals.htmlfile) | 4988 | if not os.path.isfile(sysvals.htmlfile): |
| 4795 | processData() | 4989 | doError('a directory already exists with this name: %s' % sysvals.htmlfile) |
| 4990 | elif not os.access(sysvals.htmlfile, os.W_OK): | ||
| 4991 | doError('missing permission to write to %s' % sysvals.htmlfile) | ||
| 4992 | return processData() | ||
| 4796 | 4993 | ||
| 4797 | # Function: runTest | 4994 | # Function: runTest |
| 4798 | # Description: | 4995 | # Description: |
| 4799 | # execute a suspend/resume, gather the logs, and generate the output | 4996 | # execute a suspend/resume, gather the logs, and generate the output |
| 4800 | def runTest(subdir, testpath=''): | 4997 | def runTest(): |
| 4801 | # prepare for the test | 4998 | # prepare for the test |
| 4802 | sysvals.initFtrace() | 4999 | sysvals.initFtrace() |
| 4803 | sysvals.initTestOutput(subdir, testpath) | 5000 | sysvals.initTestOutput('suspend') |
| 4804 | vprint('Output files:\n\t%s\n\t%s\n\t%s' % \ | 5001 | vprint('Output files:\n\t%s\n\t%s\n\t%s' % \ |
| 4805 | (sysvals.dmesgfile, sysvals.ftracefile, sysvals.htmlfile)) | 5002 | (sysvals.dmesgfile, sysvals.ftracefile, sysvals.htmlfile)) |
| 4806 | 5003 | ||
| @@ -4897,7 +5094,7 @@ def configFromFile(file): | |||
| 4897 | if(opt.lower() == 'verbose'): | 5094 | if(opt.lower() == 'verbose'): |
| 4898 | sysvals.verbose = checkArgBool(value) | 5095 | sysvals.verbose = checkArgBool(value) |
| 4899 | elif(opt.lower() == 'addlogs'): | 5096 | elif(opt.lower() == 'addlogs'): |
| 4900 | sysvals.addlogs = checkArgBool(value) | 5097 | sysvals.dmesglog = sysvals.ftracelog = checkArgBool(value) |
| 4901 | elif(opt.lower() == 'dev'): | 5098 | elif(opt.lower() == 'dev'): |
| 4902 | sysvals.usedevsrc = checkArgBool(value) | 5099 | sysvals.usedevsrc = checkArgBool(value) |
| 4903 | elif(opt.lower() == 'proc'): | 5100 | elif(opt.lower() == 'proc'): |
| @@ -4947,7 +5144,7 @@ def configFromFile(file): | |||
| 4947 | elif(opt.lower() == 'mincg'): | 5144 | elif(opt.lower() == 'mincg'): |
| 4948 | sysvals.mincglen = getArgFloat('-mincg', value, 0.0, 10000.0, False) | 5145 | sysvals.mincglen = getArgFloat('-mincg', value, 0.0, 10000.0, False) |
| 4949 | elif(opt.lower() == 'output-dir'): | 5146 | elif(opt.lower() == 'output-dir'): |
| 4950 | sysvals.setOutputFolder(value) | 5147 | sysvals.testdir = sysvals.setOutputFolder(value) |
| 4951 | 5148 | ||
| 4952 | if sysvals.suspendmode == 'command' and not sysvals.testcommand: | 5149 | if sysvals.suspendmode == 'command' and not sysvals.testcommand: |
| 4953 | doError('No command supplied for mode "command"') | 5150 | doError('No command supplied for mode "command"') |
| @@ -5030,8 +5227,6 @@ def configFromFile(file): | |||
| 5030 | # Description: | 5227 | # Description: |
| 5031 | # print out the help text | 5228 | # print out the help text |
| 5032 | def printHelp(): | 5229 | def printHelp(): |
| 5033 | modes = getModes() | ||
| 5034 | |||
| 5035 | print('') | 5230 | print('') |
| 5036 | print('%s v%s' % (sysvals.title, sysvals.version)) | 5231 | print('%s v%s' % (sysvals.title, sysvals.version)) |
| 5037 | print('Usage: sudo sleepgraph <options> <commands>') | 5232 | print('Usage: sudo sleepgraph <options> <commands>') |
| @@ -5048,7 +5243,7 @@ def printHelp(): | |||
| 5048 | print(' If no specific command is given, the default behavior is to initiate') | 5243 | print(' If no specific command is given, the default behavior is to initiate') |
| 5049 | print(' a suspend/resume and capture the dmesg/ftrace output as an html timeline.') | 5244 | print(' a suspend/resume and capture the dmesg/ftrace output as an html timeline.') |
| 5050 | print('') | 5245 | print('') |
| 5051 | print(' Generates output files in subdirectory: suspend-mmddyy-HHMMSS') | 5246 | print(' Generates output files in subdirectory: suspend-yymmdd-HHMMSS') |
| 5052 | print(' HTML output: <hostname>_<mode>.html') | 5247 | print(' HTML output: <hostname>_<mode>.html') |
| 5053 | print(' raw dmesg output: <hostname>_<mode>_dmesg.txt') | 5248 | print(' raw dmesg output: <hostname>_<mode>_dmesg.txt') |
| 5054 | print(' raw ftrace output: <hostname>_<mode>_ftrace.txt') | 5249 | print(' raw ftrace output: <hostname>_<mode>_ftrace.txt') |
| @@ -5058,8 +5253,9 @@ def printHelp(): | |||
| 5058 | print(' -v Print the current tool version') | 5253 | print(' -v Print the current tool version') |
| 5059 | print(' -config fn Pull arguments and config options from file fn') | 5254 | print(' -config fn Pull arguments and config options from file fn') |
| 5060 | print(' -verbose Print extra information during execution and analysis') | 5255 | print(' -verbose Print extra information during execution and analysis') |
| 5061 | print(' -m mode Mode to initiate for suspend %s (default: %s)') % (modes, sysvals.suspendmode) | 5256 | print(' -m mode Mode to initiate for suspend (default: %s)') % (sysvals.suspendmode) |
| 5062 | print(' -o subdir Override the output subdirectory') | 5257 | print(' -o name Overrides the output subdirectory name when running a new test') |
| 5258 | print(' default: suspend-{date}-{time}') | ||
| 5063 | print(' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)') | 5259 | print(' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)') |
| 5064 | print(' -addlogs Add the dmesg and ftrace logs to the html output') | 5260 | print(' -addlogs Add the dmesg and ftrace logs to the html output') |
| 5065 | print(' -srgap Add a visible gap in the timeline between sus/res (default: disabled)') | 5261 | print(' -srgap Add a visible gap in the timeline between sus/res (default: disabled)') |
| @@ -5084,17 +5280,20 @@ def printHelp(): | |||
| 5084 | print(' -cgphase P Only show callgraph data for phase P (e.g. suspend_late)') | 5280 | print(' -cgphase P Only show callgraph data for phase P (e.g. suspend_late)') |
| 5085 | print(' -cgtest N Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)') | 5281 | print(' -cgtest N Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)') |
| 5086 | print(' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)') | 5282 | print(' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)') |
| 5087 | print(' [commands]') | 5283 | print('') |
| 5088 | print(' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)') | 5284 | print('Other commands:') |
| 5089 | print(' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)') | ||
| 5090 | print(' -summary directory Create a summary of all test in this dir') | ||
| 5091 | print(' -modes List available suspend modes') | 5285 | print(' -modes List available suspend modes') |
| 5092 | print(' -status Test to see if the system is enabled to run this tool') | 5286 | print(' -status Test to see if the system is enabled to run this tool') |
| 5093 | print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table') | 5287 | print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table') |
| 5288 | print(' -sysinfo Print out system info extracted from BIOS') | ||
| 5094 | print(' -usbtopo Print out the current USB topology with power info') | 5289 | print(' -usbtopo Print out the current USB topology with power info') |
| 5095 | print(' -usbauto Enable autosuspend for all connected USB devices') | 5290 | print(' -usbauto Enable autosuspend for all connected USB devices') |
| 5096 | print(' -flist Print the list of functions currently being captured in ftrace') | 5291 | print(' -flist Print the list of functions currently being captured in ftrace') |
| 5097 | print(' -flistall Print all functions capable of being captured in ftrace') | 5292 | print(' -flistall Print all functions capable of being captured in ftrace') |
| 5293 | print(' -summary directory Create a summary of all test in this dir') | ||
| 5294 | print(' [redo]') | ||
| 5295 | print(' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)') | ||
| 5296 | print(' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)') | ||
| 5098 | print('') | 5297 | print('') |
| 5099 | return True | 5298 | return True |
| 5100 | 5299 | ||
| @@ -5102,9 +5301,9 @@ def printHelp(): | |||
| 5102 | # exec start (skipped if script is loaded as library) | 5301 | # exec start (skipped if script is loaded as library) |
| 5103 | if __name__ == '__main__': | 5302 | if __name__ == '__main__': |
| 5104 | cmd = '' | 5303 | cmd = '' |
| 5105 | cmdarg = '' | 5304 | outdir = '' |
| 5106 | multitest = {'run': False, 'count': 0, 'delay': 0} | 5305 | multitest = {'run': False, 'count': 0, 'delay': 0} |
| 5107 | simplecmds = ['-modes', '-fpdt', '-flist', '-flistall', '-usbtopo', '-usbauto', '-status'] | 5306 | simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall', '-usbtopo', '-usbauto', '-status'] |
| 5108 | # loop through the command line arguments | 5307 | # loop through the command line arguments |
| 5109 | args = iter(sys.argv[1:]) | 5308 | args = iter(sys.argv[1:]) |
| 5110 | for arg in args: | 5309 | for arg in args: |
| @@ -5135,7 +5334,7 @@ if __name__ == '__main__': | |||
| 5135 | elif(arg == '-f'): | 5334 | elif(arg == '-f'): |
| 5136 | sysvals.usecallgraph = True | 5335 | sysvals.usecallgraph = True |
| 5137 | elif(arg == '-addlogs'): | 5336 | elif(arg == '-addlogs'): |
| 5138 | sysvals.addlogs = True | 5337 | sysvals.dmesglog = sysvals.ftracelog = True |
| 5139 | elif(arg == '-verbose'): | 5338 | elif(arg == '-verbose'): |
| 5140 | sysvals.verbose = True | 5339 | sysvals.verbose = True |
| 5141 | elif(arg == '-proc'): | 5340 | elif(arg == '-proc'): |
| @@ -5195,7 +5394,7 @@ if __name__ == '__main__': | |||
| 5195 | val = args.next() | 5394 | val = args.next() |
| 5196 | except: | 5395 | except: |
| 5197 | doError('No subdirectory name supplied', True) | 5396 | doError('No subdirectory name supplied', True) |
| 5198 | sysvals.setOutputFolder(val) | 5397 | outdir = sysvals.setOutputFolder(val) |
| 5199 | elif(arg == '-config'): | 5398 | elif(arg == '-config'): |
| 5200 | try: | 5399 | try: |
| 5201 | val = args.next() | 5400 | val = args.next() |
| @@ -5236,7 +5435,7 @@ if __name__ == '__main__': | |||
| 5236 | except: | 5435 | except: |
| 5237 | doError('No directory supplied', True) | 5436 | doError('No directory supplied', True) |
| 5238 | cmd = 'summary' | 5437 | cmd = 'summary' |
| 5239 | cmdarg = val | 5438 | outdir = val |
| 5240 | sysvals.notestrun = True | 5439 | sysvals.notestrun = True |
| 5241 | if(os.path.isdir(val) == False): | 5440 | if(os.path.isdir(val) == False): |
| 5242 | doError('%s is not accesible' % val) | 5441 | doError('%s is not accesible' % val) |
| @@ -5260,11 +5459,14 @@ if __name__ == '__main__': | |||
| 5260 | sysvals.mincglen = sysvals.mindevlen | 5459 | sysvals.mincglen = sysvals.mindevlen |
| 5261 | 5460 | ||
| 5262 | # just run a utility command and exit | 5461 | # just run a utility command and exit |
| 5462 | sysvals.cpuInfo() | ||
| 5263 | if(cmd != ''): | 5463 | if(cmd != ''): |
| 5264 | if(cmd == 'status'): | 5464 | if(cmd == 'status'): |
| 5265 | statusCheck(True) | 5465 | statusCheck(True) |
| 5266 | elif(cmd == 'fpdt'): | 5466 | elif(cmd == 'fpdt'): |
| 5267 | getFPDT(True) | 5467 | getFPDT(True) |
| 5468 | elif(cmd == 'sysinfo'): | ||
| 5469 | sysvals.printSystemInfo() | ||
| 5268 | elif(cmd == 'usbtopo'): | 5470 | elif(cmd == 'usbtopo'): |
| 5269 | detectUSB() | 5471 | detectUSB() |
| 5270 | elif(cmd == 'modes'): | 5472 | elif(cmd == 'modes'): |
| @@ -5276,7 +5478,7 @@ if __name__ == '__main__': | |||
| 5276 | elif(cmd == 'usbauto'): | 5478 | elif(cmd == 'usbauto'): |
| 5277 | setUSBDevicesAuto() | 5479 | setUSBDevicesAuto() |
| 5278 | elif(cmd == 'summary'): | 5480 | elif(cmd == 'summary'): |
| 5279 | runSummary(cmdarg, True) | 5481 | runSummary(outdir, True) |
| 5280 | sys.exit() | 5482 | sys.exit() |
| 5281 | 5483 | ||
| 5282 | # if instructed, re-analyze existing data files | 5484 | # if instructed, re-analyze existing data files |
| @@ -5289,21 +5491,43 @@ if __name__ == '__main__': | |||
| 5289 | print('Check FAILED, aborting the test run!') | 5491 | print('Check FAILED, aborting the test run!') |
| 5290 | sys.exit() | 5492 | sys.exit() |
| 5291 | 5493 | ||
| 5494 | # extract mem modes and convert | ||
| 5495 | mode = sysvals.suspendmode | ||
| 5496 | if 'mem' == mode[:3]: | ||
| 5497 | if '-' in mode: | ||
| 5498 | memmode = mode.split('-')[-1] | ||
| 5499 | else: | ||
| 5500 | memmode = 'deep' | ||
| 5501 | if memmode == 'shallow': | ||
| 5502 | mode = 'standby' | ||
| 5503 | elif memmode == 's2idle': | ||
| 5504 | mode = 'freeze' | ||
| 5505 | else: | ||
| 5506 | mode = 'mem' | ||
| 5507 | sysvals.memmode = memmode | ||
| 5508 | sysvals.suspendmode = mode | ||
| 5509 | |||
| 5510 | sysvals.systemInfo(dmidecode(sysvals.mempath)) | ||
| 5511 | |||
| 5292 | if multitest['run']: | 5512 | if multitest['run']: |
| 5293 | # run multiple tests in a separate subdirectory | 5513 | # run multiple tests in a separate subdirectory |
| 5294 | s = 'x%d' % multitest['count'] | 5514 | if not outdir: |
| 5295 | if not sysvals.outdir: | 5515 | s = 'suspend-x%d' % multitest['count'] |
| 5296 | sysvals.outdir = datetime.now().strftime('suspend-'+s+'-%m%d%y-%H%M%S') | 5516 | outdir = datetime.now().strftime(s+'-%y%m%d-%H%M%S') |
| 5297 | if not os.path.isdir(sysvals.outdir): | 5517 | if not os.path.isdir(outdir): |
| 5298 | os.mkdir(sysvals.outdir) | 5518 | os.mkdir(outdir) |
| 5299 | for i in range(multitest['count']): | 5519 | for i in range(multitest['count']): |
| 5300 | if(i != 0): | 5520 | if(i != 0): |
| 5301 | print('Waiting %d seconds...' % (multitest['delay'])) | 5521 | print('Waiting %d seconds...' % (multitest['delay'])) |
| 5302 | time.sleep(multitest['delay']) | 5522 | time.sleep(multitest['delay']) |
| 5303 | print('TEST (%d/%d) START' % (i+1, multitest['count'])) | 5523 | print('TEST (%d/%d) START' % (i+1, multitest['count'])) |
| 5304 | runTest(sysvals.outdir) | 5524 | fmt = 'suspend-%y%m%d-%H%M%S' |
| 5525 | sysvals.testdir = os.path.join(outdir, datetime.now().strftime(fmt)) | ||
| 5526 | runTest() | ||
| 5305 | print('TEST (%d/%d) COMPLETE' % (i+1, multitest['count'])) | 5527 | print('TEST (%d/%d) COMPLETE' % (i+1, multitest['count'])) |
| 5306 | runSummary(sysvals.outdir, False) | 5528 | runSummary(outdir, False) |
| 5307 | else: | 5529 | else: |
| 5530 | if outdir: | ||
| 5531 | sysvals.testdir = outdir | ||
| 5308 | # run the test in the current directory | 5532 | # run the test in the current directory |
| 5309 | runTest('.', sysvals.outdir) | 5533 | runTest() |
diff --git a/tools/power/pm-graph/bootgraph.8 b/tools/power/pm-graph/bootgraph.8 index 55272a67b0e7..dbdafcf546df 100644 --- a/tools/power/pm-graph/bootgraph.8 +++ b/tools/power/pm-graph/bootgraph.8 | |||
| @@ -8,14 +8,23 @@ bootgraph \- Kernel boot timing analysis | |||
| 8 | .RB [ COMMAND ] | 8 | .RB [ COMMAND ] |
| 9 | .SH DESCRIPTION | 9 | .SH DESCRIPTION |
| 10 | \fBbootgraph \fP reads the dmesg log from kernel boot and | 10 | \fBbootgraph \fP reads the dmesg log from kernel boot and |
| 11 | creates an html representation of the initcall timeline up to the start | 11 | creates an html representation of the initcall timeline. It graphs |
| 12 | of the init process. | 12 | every module init call found, through both kernel and user modes. The |
| 13 | timeline is split into two phases: kernel mode & user mode. kernel mode | ||
| 14 | represents a single process run on a single cpu with serial init calls. | ||
| 15 | Once user mode begins, the init process is called, and the init calls | ||
| 16 | start working in parallel. | ||
| 13 | .PP | 17 | .PP |
| 14 | If no specific command is given, the tool reads the current dmesg log and | 18 | If no specific command is given, the tool reads the current dmesg log and |
| 15 | outputs bootgraph.html. | 19 | outputs a new timeline. |
| 16 | .PP | 20 | .PP |
| 17 | The tool can also augment the timeline with ftrace data on custom target | 21 | The tool can also augment the timeline with ftrace data on custom target |
| 18 | functions as well as full trace callgraphs. | 22 | functions as well as full trace callgraphs. |
| 23 | .PP | ||
| 24 | Generates output files in subdirectory: boot-yymmdd-HHMMSS | ||
| 25 | html timeline : <hostname>_boot.html | ||
| 26 | raw dmesg file : <hostname>_boot_dmesg.txt | ||
| 27 | raw ftrace file : <hostname>_boot_ftrace.txt | ||
| 19 | .SH OPTIONS | 28 | .SH OPTIONS |
| 20 | .TP | 29 | .TP |
| 21 | \fB-h\fR | 30 | \fB-h\fR |
| @@ -28,15 +37,18 @@ Print the current tool version | |||
| 28 | Add the dmesg log to the html output. It will be viewable by | 37 | Add the dmesg log to the html output. It will be viewable by |
| 29 | clicking a button in the timeline. | 38 | clicking a button in the timeline. |
| 30 | .TP | 39 | .TP |
| 31 | \fB-o \fIfile\fR | 40 | \fB-o \fIname\fR |
| 32 | Override the HTML output filename (default: bootgraph.html) | 41 | Overrides the output subdirectory name when running a new test. |
| 33 | .SS "Ftrace Debug" | 42 | Use {date}, {time}, {hostname} for current values. |
| 43 | .sp | ||
| 44 | e.g. boot-{hostname}-{date}-{time} | ||
| 45 | .SS "advanced" | ||
| 34 | .TP | 46 | .TP |
| 35 | \fB-f\fR | 47 | \fB-f\fR |
| 36 | Use ftrace to add function detail (default: disabled) | 48 | Use ftrace to add function detail (default: disabled) |
| 37 | .TP | 49 | .TP |
| 38 | \fB-callgraph\fR | 50 | \fB-callgraph\fR |
| 39 | Use ftrace to create initcall callgraphs (default: disabled). If -filter | 51 | Use ftrace to create initcall callgraphs (default: disabled). If -func |
| 40 | is not used there will be one callgraph per initcall. This can produce | 52 | is not used there will be one callgraph per initcall. This can produce |
| 41 | very large outputs, i.e. 10MB - 100MB. | 53 | very large outputs, i.e. 10MB - 100MB. |
| 42 | .TP | 54 | .TP |
| @@ -50,16 +62,19 @@ This reduces the html file size as there can be many tiny callgraphs | |||
| 50 | which are barely visible in the timeline. | 62 | which are barely visible in the timeline. |
| 51 | The value is a float: e.g. 0.001 represents 1 us. | 63 | The value is a float: e.g. 0.001 represents 1 us. |
| 52 | .TP | 64 | .TP |
| 65 | \fB-cgfilter \fI"func1,func2,..."\fR | ||
| 66 | Reduce callgraph output in the timeline by limiting it to a list of calls. The | ||
| 67 | argument can be a single function name or a comma delimited list. | ||
| 68 | (default: none) | ||
| 69 | .TP | ||
| 53 | \fB-timeprec \fIn\fR | 70 | \fB-timeprec \fIn\fR |
| 54 | Number of significant digits in timestamps (0:S, 3:ms, [6:us]) | 71 | Number of significant digits in timestamps (0:S, 3:ms, [6:us]) |
| 55 | .TP | 72 | .TP |
| 56 | \fB-expandcg\fR | 73 | \fB-expandcg\fR |
| 57 | pre-expand the callgraph data in the html output (default: disabled) | 74 | pre-expand the callgraph data in the html output (default: disabled) |
| 58 | .TP | 75 | .TP |
| 59 | \fB-filter \fI"func1,func2,..."\fR | 76 | \fB-func \fI"func1,func2,..."\fR |
| 60 | Instead of tracing each initcall, trace a custom list of functions (default: do_one_initcall) | 77 | Instead of tracing each initcall, trace a custom list of functions (default: do_one_initcall) |
| 61 | |||
| 62 | .SH COMMANDS | ||
| 63 | .TP | 78 | .TP |
| 64 | \fB-reboot\fR | 79 | \fB-reboot\fR |
| 65 | Reboot the machine and generate a new timeline automatically. Works in 4 steps. | 80 | Reboot the machine and generate a new timeline automatically. Works in 4 steps. |
| @@ -73,16 +88,23 @@ Show the requirements to generate a new timeline manually. Requires 3 steps. | |||
| 73 | 1. append the string to the kernel command line via your native boot manager. | 88 | 1. append the string to the kernel command line via your native boot manager. |
| 74 | 2. reboot the system | 89 | 2. reboot the system |
| 75 | 3. after startup, re-run the tool with the same arguments and no command | 90 | 3. after startup, re-run the tool with the same arguments and no command |
| 91 | |||
| 92 | .SH COMMANDS | ||
| 93 | .SS "rebuild" | ||
| 76 | .TP | 94 | .TP |
| 77 | \fB-dmesg \fIfile\fR | 95 | \fB-dmesg \fIfile\fR |
| 78 | Create HTML output from an existing dmesg file. | 96 | Create HTML output from an existing dmesg file. |
| 79 | .TP | 97 | .TP |
| 80 | \fB-ftrace \fIfile\fR | 98 | \fB-ftrace \fIfile\fR |
| 81 | Create HTML output from an existing ftrace file (used with -dmesg). | 99 | Create HTML output from an existing ftrace file (used with -dmesg). |
| 100 | .SS "other" | ||
| 82 | .TP | 101 | .TP |
| 83 | \fB-flistall\fR | 102 | \fB-flistall\fR |
| 84 | Print all ftrace functions capable of being captured. These are all the | 103 | Print all ftrace functions capable of being captured. These are all the |
| 85 | possible values you can add to trace via the -filter argument. | 104 | possible values you can add to trace via the -func argument. |
| 105 | .TP | ||
| 106 | \fB-sysinfo\fR | ||
| 107 | Print out system info extracted from BIOS. Reads /dev/mem directly instead of going through dmidecode. | ||
| 86 | 108 | ||
| 87 | .SH EXAMPLES | 109 | .SH EXAMPLES |
| 88 | Create a timeline using the current dmesg log. | 110 | Create a timeline using the current dmesg log. |
| @@ -93,13 +115,13 @@ Create a timeline using the current dmesg and ftrace log. | |||
| 93 | .IP | 115 | .IP |
| 94 | \f(CW$ bootgraph -callgraph\fR | 116 | \f(CW$ bootgraph -callgraph\fR |
| 95 | .PP | 117 | .PP |
| 96 | Create a timeline using the current dmesg, add the log to the html and change the name. | 118 | Create a timeline using the current dmesg, add the log to the html and change the folder. |
| 97 | .IP | 119 | .IP |
| 98 | \f(CW$ bootgraph -addlogs -o myboot.html\fR | 120 | \f(CW$ bootgraph -addlogs -o "myboot-{date}-{time}"\fR |
| 99 | .PP | 121 | .PP |
| 100 | Capture a new boot timeline by automatically rebooting the machine. | 122 | Capture a new boot timeline by automatically rebooting the machine. |
| 101 | .IP | 123 | .IP |
| 102 | \f(CW$ sudo bootgraph -reboot -addlogs -o latestboot.html\fR | 124 | \f(CW$ sudo bootgraph -reboot -addlogs -o "latest-{hostname)"\fR |
| 103 | .PP | 125 | .PP |
| 104 | Capture a new boot timeline with function trace data. | 126 | Capture a new boot timeline with function trace data. |
| 105 | .IP | 127 | .IP |
| @@ -111,7 +133,7 @@ Capture a new boot timeline with trace & callgraph data. Skip callgraphs smaller | |||
| 111 | .PP | 133 | .PP |
| 112 | Capture a new boot timeline with callgraph data over custom functions. | 134 | Capture a new boot timeline with callgraph data over custom functions. |
| 113 | .IP | 135 | .IP |
| 114 | \f(CW$ sudo bootgraph -reboot -callgraph -filter "acpi_ps_parse_aml,msleep"\fR | 136 | \f(CW$ sudo bootgraph -reboot -callgraph -func "acpi_ps_parse_aml,msleep"\fR |
| 115 | .PP | 137 | .PP |
| 116 | Capture a brand new boot timeline with manual reboot. | 138 | Capture a brand new boot timeline with manual reboot. |
| 117 | .IP | 139 | .IP |
| @@ -123,6 +145,15 @@ Capture a brand new boot timeline with manual reboot. | |||
| 123 | .IP | 145 | .IP |
| 124 | \f(CW$ sudo bootgraph -callgraph # re-run the tool after restart\fR | 146 | \f(CW$ sudo bootgraph -callgraph # re-run the tool after restart\fR |
| 125 | .PP | 147 | .PP |
| 148 | .SS "rebuild timeline from logs" | ||
| 149 | .PP | ||
| 150 | Rebuild the html from a previous run's logs, using the same options. | ||
| 151 | .IP | ||
| 152 | \f(CW$ bootgraph -dmesg dmesg.txt -ftrace ftrace.txt -callgraph\fR | ||
| 153 | .PP | ||
| 154 | Rebuild the html with different options. | ||
| 155 | .IP | ||
| 156 | \f(CW$ bootgraph -dmesg dmesg.txt -ftrace ftrace.txt -addlogs\fR | ||
| 126 | 157 | ||
| 127 | .SH "SEE ALSO" | 158 | .SH "SEE ALSO" |
| 128 | dmesg(1), update-grub(8), crontab(1), reboot(8) | 159 | dmesg(1), update-grub(8), crontab(1), reboot(8) |
diff --git a/tools/power/pm-graph/sleepgraph.8 b/tools/power/pm-graph/sleepgraph.8 index 610e72ebbc06..fbe7bd3eae8e 100644 --- a/tools/power/pm-graph/sleepgraph.8 +++ b/tools/power/pm-graph/sleepgraph.8 | |||
| @@ -39,8 +39,9 @@ Pull arguments and config options from a file. | |||
| 39 | \fB-m \fImode\fR | 39 | \fB-m \fImode\fR |
| 40 | Mode to initiate for suspend e.g. standby, freeze, mem (default: mem). | 40 | Mode to initiate for suspend e.g. standby, freeze, mem (default: mem). |
| 41 | .TP | 41 | .TP |
| 42 | \fB-o \fIsubdir\fR | 42 | \fB-o \fIname\fR |
| 43 | Override the output subdirectory. Use {date}, {time}, {hostname} for current values. | 43 | Overrides the output subdirectory name when running a new test. |
| 44 | Use {date}, {time}, {hostname} for current values. | ||
| 44 | .sp | 45 | .sp |
| 45 | e.g. suspend-{hostname}-{date}-{time} | 46 | e.g. suspend-{hostname}-{date}-{time} |
| 46 | .TP | 47 | .TP |
| @@ -52,7 +53,7 @@ disable rtcwake and require a user keypress to resume. | |||
| 52 | Add the dmesg and ftrace logs to the html output. They will be viewable by | 53 | Add the dmesg and ftrace logs to the html output. They will be viewable by |
| 53 | clicking buttons in the timeline. | 54 | clicking buttons in the timeline. |
| 54 | 55 | ||
| 55 | .SS "Advanced" | 56 | .SS "advanced" |
| 56 | .TP | 57 | .TP |
| 57 | \fB-cmd \fIstr\fR | 58 | \fB-cmd \fIstr\fR |
| 58 | Run the timeline over a custom suspend command, e.g. pm-suspend. By default | 59 | Run the timeline over a custom suspend command, e.g. pm-suspend. By default |
| @@ -91,7 +92,7 @@ Include \fIt\fR ms delay after last resume (default: 0 ms). | |||
| 91 | Execute \fIn\fR consecutive tests at \fId\fR seconds intervals. The outputs will | 92 | Execute \fIn\fR consecutive tests at \fId\fR seconds intervals. The outputs will |
| 92 | be created in a new subdirectory with a summary page: suspend-xN-{date}-{time}. | 93 | be created in a new subdirectory with a summary page: suspend-xN-{date}-{time}. |
| 93 | 94 | ||
| 94 | .SS "Ftrace Debug" | 95 | .SS "ftrace debug" |
| 95 | .TP | 96 | .TP |
| 96 | \fB-f\fR | 97 | \fB-f\fR |
| 97 | Use ftrace to create device callgraphs (default: disabled). This can produce | 98 | Use ftrace to create device callgraphs (default: disabled). This can produce |
| @@ -124,12 +125,6 @@ Number of significant digits in timestamps (0:S, [3:ms], 6:us). | |||
| 124 | 125 | ||
| 125 | .SH COMMANDS | 126 | .SH COMMANDS |
| 126 | .TP | 127 | .TP |
| 127 | \fB-ftrace \fIfile\fR | ||
| 128 | Create HTML output from an existing ftrace file. | ||
| 129 | .TP | ||
| 130 | \fB-dmesg \fIfile\fR | ||
| 131 | Create HTML output from an existing dmesg file. | ||
| 132 | .TP | ||
| 133 | \fB-summary \fIindir\fR | 128 | \fB-summary \fIindir\fR |
| 134 | Create a summary page of all tests in \fIindir\fR. Creates summary.html | 129 | Create a summary page of all tests in \fIindir\fR. Creates summary.html |
| 135 | in the current folder. The output page is a table of tests with | 130 | in the current folder. The output page is a table of tests with |
| @@ -146,6 +141,9 @@ with any options you intend to use to see if they will work. | |||
| 146 | \fB-fpdt\fR | 141 | \fB-fpdt\fR |
| 147 | Print out the contents of the ACPI Firmware Performance Data Table. | 142 | Print out the contents of the ACPI Firmware Performance Data Table. |
| 148 | .TP | 143 | .TP |
| 144 | \fB-sysinfo\fR | ||
| 145 | Print out system info extracted from BIOS. Reads /dev/mem directly instead of going through dmidecode. | ||
| 146 | .TP | ||
| 149 | \fB-usbtopo\fR | 147 | \fB-usbtopo\fR |
| 150 | Print out the current USB topology with power info. | 148 | Print out the current USB topology with power info. |
| 151 | .TP | 149 | .TP |
| @@ -162,9 +160,16 @@ with -fadd they will also be checked. | |||
| 162 | \fB-flistall\fR | 160 | \fB-flistall\fR |
| 163 | Print all ftrace functions capable of being captured. These are all the | 161 | Print all ftrace functions capable of being captured. These are all the |
| 164 | possible values you can add to trace via the -fadd argument. | 162 | possible values you can add to trace via the -fadd argument. |
| 163 | .SS "rebuild" | ||
| 164 | .TP | ||
| 165 | \fB-ftrace \fIfile\fR | ||
| 166 | Create HTML output from an existing ftrace file. | ||
| 167 | .TP | ||
| 168 | \fB-dmesg \fIfile\fR | ||
| 169 | Create HTML output from an existing dmesg file. | ||
| 165 | 170 | ||
| 166 | .SH EXAMPLES | 171 | .SH EXAMPLES |
| 167 | .SS "Simple Commands" | 172 | .SS "simple commands" |
| 168 | Check which suspend modes are currently supported. | 173 | Check which suspend modes are currently supported. |
| 169 | .IP | 174 | .IP |
| 170 | \f(CW$ sleepgraph -modes\fR | 175 | \f(CW$ sleepgraph -modes\fR |
| @@ -185,12 +190,8 @@ Generate a summary of all timelines in a particular folder. | |||
| 185 | .IP | 190 | .IP |
| 186 | \f(CW$ sleepgraph -summary ~/workspace/myresults/\fR | 191 | \f(CW$ sleepgraph -summary ~/workspace/myresults/\fR |
| 187 | .PP | 192 | .PP |
| 188 | Re-generate the html output from a previous run's dmesg and ftrace log. | ||
| 189 | .IP | ||
| 190 | \f(CW$ sleepgraph -dmesg myhost_mem_dmesg.txt -ftrace myhost_mem_ftrace.txt\fR | ||
| 191 | .PP | ||
| 192 | 193 | ||
| 193 | .SS "Capturing Simple Timelines" | 194 | .SS "capturing basic timelines" |
| 194 | Execute a mem suspend with a 15 second wakeup. Include the logs in the html. | 195 | Execute a mem suspend with a 15 second wakeup. Include the logs in the html. |
| 195 | .IP | 196 | .IP |
| 196 | \f(CW$ sudo sleepgraph -rtcwake 15 -addlogs\fR | 197 | \f(CW$ sudo sleepgraph -rtcwake 15 -addlogs\fR |
| @@ -204,7 +205,7 @@ Execute a freeze with no wakeup (require keypress). Change output folder name. | |||
| 204 | \f(CW$ sudo sleepgraph -m freeze -rtcwake off -o "freeze-{hostname}-{date}-{time}"\fR | 205 | \f(CW$ sudo sleepgraph -m freeze -rtcwake off -o "freeze-{hostname}-{date}-{time}"\fR |
| 205 | .PP | 206 | .PP |
| 206 | 207 | ||
| 207 | .SS "Capturing Advanced Timelines" | 208 | .SS "capturing advanced timelines" |
| 208 | Execute a suspend & include dev mode source calls, limit callbacks to 5ms or larger. | 209 | Execute a suspend & include dev mode source calls, limit callbacks to 5ms or larger. |
| 209 | .IP | 210 | .IP |
| 210 | \f(CW$ sudo sleepgraph -m mem -rtcwake 15 -dev -mindev 5\fR | 211 | \f(CW$ sudo sleepgraph -m mem -rtcwake 15 -dev -mindev 5\fR |
| @@ -222,8 +223,7 @@ Execute a suspend using a custom command. | |||
| 222 | \f(CW$ sudo sleepgraph -cmd "echo mem > /sys/power/state" -rtcwake 15\fR | 223 | \f(CW$ sudo sleepgraph -cmd "echo mem > /sys/power/state" -rtcwake 15\fR |
| 223 | .PP | 224 | .PP |
| 224 | 225 | ||
| 225 | 226 | .SS "adding callgraph data" | |
| 226 | .SS "Capturing Timelines with Callgraph Data" | ||
| 227 | Add device callgraphs. Limit the trace depth and only show callgraphs 10ms or larger. | 227 | Add device callgraphs. Limit the trace depth and only show callgraphs 10ms or larger. |
| 228 | .IP | 228 | .IP |
| 229 | \f(CW$ sudo sleepgraph -m mem -rtcwake 15 -f -maxdepth 5 -mincg 10\fR | 229 | \f(CW$ sudo sleepgraph -m mem -rtcwake 15 -f -maxdepth 5 -mincg 10\fR |
| @@ -235,6 +235,16 @@ Capture a full callgraph across all suspend, then filter the html by a single ph | |||
| 235 | \f(CW$ sleepgraph -dmesg host_mem_dmesg.txt -ftrace host_mem_ftrace.txt -f -cgphase resume | 235 | \f(CW$ sleepgraph -dmesg host_mem_dmesg.txt -ftrace host_mem_ftrace.txt -f -cgphase resume |
| 236 | .PP | 236 | .PP |
| 237 | 237 | ||
| 238 | .SS "rebuild timeline from logs" | ||
| 239 | .PP | ||
| 240 | Rebuild the html from a previous run's logs, using the same options. | ||
| 241 | .IP | ||
| 242 | \f(CW$ sleepgraph -dmesg dmesg.txt -ftrace ftrace.txt -callgraph\fR | ||
| 243 | .PP | ||
| 244 | Rebuild the html with different options. | ||
| 245 | .IP | ||
| 246 | \f(CW$ sleepgraph -dmesg dmesg.txt -ftrace ftrace.txt -addlogs -srgap\fR | ||
| 247 | |||
| 238 | .SH "SEE ALSO" | 248 | .SH "SEE ALSO" |
| 239 | dmesg(1) | 249 | dmesg(1) |
| 240 | .PP | 250 | .PP |
