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 /tools | |
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
Diffstat (limited to 'tools')
-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 |