aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-09-03 18:07:36 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-09-03 18:07:36 -0400
commitd97561f461e4cb5b2e170bc33b466cffbddc8719 (patch)
tree7be9749e40f2db48c0cebbc1d0eeadbda36c7ce9 /tools
parent4afbce7b39d2b326616f0a87a6246b1383237868 (diff)
parentd0e4a193c33adaa4f91128d5393aa3589c2f3e9e (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.c15
-rw-r--r--tools/power/cpupower/utils/helpers/cpuid.c4
-rw-r--r--tools/power/cpupower/utils/helpers/helpers.h5
-rw-r--r--tools/power/cpupower/utils/helpers/misc.c2
-rw-r--r--tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c4
-rw-r--r--tools/power/cpupower/utils/idle_monitor/mperf_monitor.c3
-rw-r--r--tools/power/cpupower/utils/idle_monitor/nhm_idle.c8
-rw-r--r--tools/power/cpupower/utils/idle_monitor/snb_idle.c4
-rw-r--r--tools/power/pm-graph/Makefile19
-rwxr-xr-xtools/power/pm-graph/analyze_boot.py586
-rwxr-xr-xtools/power/pm-graph/analyze_suspend.py534
-rw-r--r--tools/power/pm-graph/bootgraph.861
-rw-r--r--tools/power/pm-graph/sleepgraph.848
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 */
32struct cpupower_cpu_info cpupower_cpu_info; 33struct cpupower_cpu_info cpupower_cpu_info;
33int run_as_root; 34int run_as_root;
35int base_cpu;
34/* Affected cpus chosen by -c/--cpu param */ 36/* Affected cpus chosen by -c/--cpu param */
35struct bitmask *cpus_chosen; 37struct 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 */
45int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info) 45int 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
36extern int run_as_root; 36extern int run_as_root;
37extern int base_cpu;
37extern struct bitmask *cpus_chosen; 38extern 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 */
94extern int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info); 95extern int get_cpu_info(struct cpupower_cpu_info *cpu_info);
95extern struct cpupower_cpu_info cpupower_cpu_info; 96extern 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;
80static int mperf_get_tsc(unsigned long long *tsc) 80static 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 ?=
4all: 4all:
5 @echo "Nothing to build" 5 @echo "Nothing to build"
6 6
7install : 7install : 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
19uninstall : 19uninstall :
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
43class SystemValues(aslib.SystemValues): 43class 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
125sysvals = SystemValues() 203sysvals = 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
193def loadKernelLog(): 277def 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
254def loadTraceLog(data): 355def 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
403def 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
356def createBootGraph(data, embedded): 464def 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('<', '&lt').replace('>', '&gt') 638 line = line.replace('<', '&lt').replace('>', '&gt')
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
768def 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
661def printHelp(): 788def 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():
698if __name__ == '__main__': 836if __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
69class SystemValues: 69class 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
645sysvals = SystemValues() 700sysvals = SystemValues()
646suspendmodename = { 701suspendmodename = {
@@ -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}">&nbsp;{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
1996class TestProps: 2067class 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
2099def 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&rarr;</div>\n' 3390 html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">ERROR&rarr;</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}">&nbsp;{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, '_', ' &nbsp;') 3660 name = string.replace(phase, '_', ' &nbsp;')
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
4231def 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
4246def setUSBDevicesAuto(): 4316def 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
4469def getModes(): 4539def 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
4567def 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
4722def 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
4800def runTest(subdir, testpath=''): 4997def 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
5032def printHelp(): 5229def 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)
5103if __name__ == '__main__': 5302if __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
11creates an html representation of the initcall timeline up to the start 11creates an html representation of the initcall timeline. It graphs
12of the init process. 12every module init call found, through both kernel and user modes. The
13timeline is split into two phases: kernel mode & user mode. kernel mode
14represents a single process run on a single cpu with serial init calls.
15Once user mode begins, the init process is called, and the init calls
16start working in parallel.
13.PP 17.PP
14If no specific command is given, the tool reads the current dmesg log and 18If no specific command is given, the tool reads the current dmesg log and
15outputs bootgraph.html. 19outputs a new timeline.
16.PP 20.PP
17The tool can also augment the timeline with ftrace data on custom target 21The tool can also augment the timeline with ftrace data on custom target
18functions as well as full trace callgraphs. 22functions as well as full trace callgraphs.
23.PP
24Generates 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
28Add the dmesg log to the html output. It will be viewable by 37Add the dmesg log to the html output. It will be viewable by
29clicking a button in the timeline. 38clicking a button in the timeline.
30.TP 39.TP
31\fB-o \fIfile\fR 40\fB-o \fIname\fR
32Override the HTML output filename (default: bootgraph.html) 41Overrides the output subdirectory name when running a new test.
33.SS "Ftrace Debug" 42Use {date}, {time}, {hostname} for current values.
43.sp
44e.g. boot-{hostname}-{date}-{time}
45.SS "advanced"
34.TP 46.TP
35\fB-f\fR 47\fB-f\fR
36Use ftrace to add function detail (default: disabled) 48Use ftrace to add function detail (default: disabled)
37.TP 49.TP
38\fB-callgraph\fR 50\fB-callgraph\fR
39Use ftrace to create initcall callgraphs (default: disabled). If -filter 51Use ftrace to create initcall callgraphs (default: disabled). If -func
40is not used there will be one callgraph per initcall. This can produce 52is not used there will be one callgraph per initcall. This can produce
41very large outputs, i.e. 10MB - 100MB. 53very 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
50which are barely visible in the timeline. 62which are barely visible in the timeline.
51The value is a float: e.g. 0.001 represents 1 us. 63The value is a float: e.g. 0.001 represents 1 us.
52.TP 64.TP
65\fB-cgfilter \fI"func1,func2,..."\fR
66Reduce callgraph output in the timeline by limiting it to a list of calls. The
67argument 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
54Number of significant digits in timestamps (0:S, 3:ms, [6:us]) 71Number of significant digits in timestamps (0:S, 3:ms, [6:us])
55.TP 72.TP
56\fB-expandcg\fR 73\fB-expandcg\fR
57pre-expand the callgraph data in the html output (default: disabled) 74pre-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
60Instead of tracing each initcall, trace a custom list of functions (default: do_one_initcall) 77Instead 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
65Reboot the machine and generate a new timeline automatically. Works in 4 steps. 80Reboot 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
78Create HTML output from an existing dmesg file. 96Create HTML output from an existing dmesg file.
79.TP 97.TP
80\fB-ftrace \fIfile\fR 98\fB-ftrace \fIfile\fR
81Create HTML output from an existing ftrace file (used with -dmesg). 99Create 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
84Print all ftrace functions capable of being captured. These are all the 103Print all ftrace functions capable of being captured. These are all the
85possible values you can add to trace via the -filter argument. 104possible values you can add to trace via the -func argument.
105.TP
106\fB-sysinfo\fR
107Print out system info extracted from BIOS. Reads /dev/mem directly instead of going through dmidecode.
86 108
87.SH EXAMPLES 109.SH EXAMPLES
88Create a timeline using the current dmesg log. 110Create 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
96Create a timeline using the current dmesg, add the log to the html and change the name. 118Create 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
100Capture a new boot timeline by automatically rebooting the machine. 122Capture 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
104Capture a new boot timeline with function trace data. 126Capture 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
112Capture a new boot timeline with callgraph data over custom functions. 134Capture 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
116Capture a brand new boot timeline with manual reboot. 138Capture 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
150Rebuild 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
154Rebuild 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"
128dmesg(1), update-grub(8), crontab(1), reboot(8) 159dmesg(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
40Mode to initiate for suspend e.g. standby, freeze, mem (default: mem). 40Mode 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
43Override the output subdirectory. Use {date}, {time}, {hostname} for current values. 43Overrides the output subdirectory name when running a new test.
44Use {date}, {time}, {hostname} for current values.
44.sp 45.sp
45e.g. suspend-{hostname}-{date}-{time} 46e.g. suspend-{hostname}-{date}-{time}
46.TP 47.TP
@@ -52,7 +53,7 @@ disable rtcwake and require a user keypress to resume.
52Add the dmesg and ftrace logs to the html output. They will be viewable by 53Add the dmesg and ftrace logs to the html output. They will be viewable by
53clicking buttons in the timeline. 54clicking 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
58Run the timeline over a custom suspend command, e.g. pm-suspend. By default 59Run 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).
91Execute \fIn\fR consecutive tests at \fId\fR seconds intervals. The outputs will 92Execute \fIn\fR consecutive tests at \fId\fR seconds intervals. The outputs will
92be created in a new subdirectory with a summary page: suspend-xN-{date}-{time}. 93be 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
97Use ftrace to create device callgraphs (default: disabled). This can produce 98Use 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
128Create HTML output from an existing ftrace file.
129.TP
130\fB-dmesg \fIfile\fR
131Create HTML output from an existing dmesg file.
132.TP
133\fB-summary \fIindir\fR 128\fB-summary \fIindir\fR
134Create a summary page of all tests in \fIindir\fR. Creates summary.html 129Create a summary page of all tests in \fIindir\fR. Creates summary.html
135in the current folder. The output page is a table of tests with 130in 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
147Print out the contents of the ACPI Firmware Performance Data Table. 142Print out the contents of the ACPI Firmware Performance Data Table.
148.TP 143.TP
144\fB-sysinfo\fR
145Print 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
150Print out the current USB topology with power info. 148Print 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
163Print all ftrace functions capable of being captured. These are all the 161Print all ftrace functions capable of being captured. These are all the
164possible values you can add to trace via the -fadd argument. 162possible values you can add to trace via the -fadd argument.
163.SS "rebuild"
164.TP
165\fB-ftrace \fIfile\fR
166Create HTML output from an existing ftrace file.
167.TP
168\fB-dmesg \fIfile\fR
169Create HTML output from an existing dmesg file.
165 170
166.SH EXAMPLES 171.SH EXAMPLES
167.SS "Simple Commands" 172.SS "simple commands"
168Check which suspend modes are currently supported. 173Check 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
188Re-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"
194Execute a mem suspend with a 15 second wakeup. Include the logs in the html. 195Execute 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"
208Execute a suspend & include dev mode source calls, limit callbacks to 5ms or larger. 209Execute 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"
227Add device callgraphs. Limit the trace depth and only show callgraphs 10ms or larger. 227Add 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
240Rebuild 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
244Rebuild 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"
239dmesg(1) 249dmesg(1)
240.PP 250.PP