Commit 3a608ca01d76e8cb90dcc8dc1a22cde98cdca3ab
Committed by
Wolfgang Denk
1 parent
770605e4f9
Exists in
master
and in
54 other branches
bootstage: Implement core microsecond boot time measurement
This defines the basics of a new boot time measurement feature. This allows logging of very accurate time measurements as the boot proceeds, by using an available microsecond counter. To enable the feature, define CONFIG_BOOTSTAGE in your board config file. Also available is CONFIG_BOOTSTAGE_REPORT which will cause a report to be printed just before handing off to the OS. Most IDs are not named at this stage. For that I would first like to renumber them all. Timer summary in microseconds: Mark Elapsed Stage 0 0 reset 205,000 205,000 board_init_f 6,053,000 5,848,000 bootm_start 6,053,000 0 id=1 6,058,000 5,000 id=101 6,058,000 0 id=100 6,061,000 3,000 id=103 6,064,000 3,000 id=104 6,093,000 29,000 id=107 6,093,000 0 id=106 6,093,000 0 id=105 6,093,000 0 id=108 7,089,000 996,000 id=7 7,089,000 0 id=15 7,089,000 0 id=8 7,097,000 8,000 start_kernel Signed-off-by: Simon Glass <sjg@chromium.org>
Showing 4 changed files with 227 additions and 0 deletions Side-by-side Diff
README
... | ... | @@ -2261,6 +2261,31 @@ |
2261 | 2261 | example, some LED's) on your board. At the moment, |
2262 | 2262 | the following checkpoints are implemented: |
2263 | 2263 | |
2264 | +- Detailed boot stage timing | |
2265 | + CONFIG_BOOTSTAGE | |
2266 | + Define this option to get detailed timing of each stage | |
2267 | + of the boot process. | |
2268 | + | |
2269 | + CONFIG_BOOTSTAGE_USER_COUNT | |
2270 | + This is the number of available user bootstage records. | |
2271 | + Each time you call bootstage_mark(BOOTSTAGE_ID_ALLOC, ...) | |
2272 | + a new ID will be allocated from this stash. If you exceed | |
2273 | + the limit, recording will stop. | |
2274 | + | |
2275 | + CONFIG_BOOTSTAGE_REPORT | |
2276 | + Define this to print a report before boot, similar to this: | |
2277 | + | |
2278 | + Timer summary in microseconds: | |
2279 | + Mark Elapsed Stage | |
2280 | + 0 0 reset | |
2281 | + 3,575,678 3,575,678 board_init_f start | |
2282 | + 3,575,695 17 arch_cpu_init A9 | |
2283 | + 3,575,777 82 arch_cpu_init done | |
2284 | + 3,659,598 83,821 board_init_r start | |
2285 | + 3,910,375 250,777 main_loop | |
2286 | + 29,916,167 26,005,792 bootm_start | |
2287 | + 30,361,327 445,160 start_kernel | |
2288 | + | |
2264 | 2289 | Legacy uImage format: |
2265 | 2290 | |
2266 | 2291 | Arg Where When |
common/Makefile
common/bootstage.c
1 | +/* | |
2 | + * Copyright (c) 2011, Google Inc. All rights reserved. | |
3 | + * | |
4 | + * See file CREDITS for list of people who contributed to this | |
5 | + * project. | |
6 | + * | |
7 | + * This program is free software; you can redistribute it and/or | |
8 | + * modify it under the terms of the GNU General Public License as | |
9 | + * published by the Free Software Foundation; either version 2 of | |
10 | + * the License, or (at your option) any later version. | |
11 | + * | |
12 | + * This program is distributed in the hope that it will be useful, | |
13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | + * GNU General Public License for more details. | |
16 | + * | |
17 | + * You should have received a copy of the GNU General Public License | |
18 | + * along with this program; if not, write to the Free Software | |
19 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
20 | + * MA 02111-1307 USA | |
21 | + */ | |
22 | + | |
23 | + | |
24 | +/* | |
25 | + * This module records the progress of boot and arbitrary commands, and | |
26 | + * permits accurate timestamping of each. | |
27 | + * | |
28 | + * TBD: Pass timings to kernel in the FDT | |
29 | + */ | |
30 | + | |
31 | +#include <common.h> | |
32 | +#include <libfdt.h> | |
33 | + | |
34 | +DECLARE_GLOBAL_DATA_PTR; | |
35 | + | |
36 | +enum bootstage_flags { | |
37 | + BOOTSTAGEF_ERROR = 1 << 0, /* Error record */ | |
38 | + BOOTSTAGEF_ALLOC = 1 << 1, /* Allocate an id */ | |
39 | +}; | |
40 | + | |
41 | +struct bootstage_record { | |
42 | + ulong time_us; | |
43 | + const char *name; | |
44 | + int flags; /* see enum bootstage_flags */ | |
45 | + enum bootstage_id id; | |
46 | +}; | |
47 | + | |
48 | +static struct bootstage_record record[BOOTSTAGE_ID_COUNT] = { {1} }; | |
49 | +static int next_id = BOOTSTAGE_ID_USER; | |
50 | + | |
51 | +ulong bootstage_add_record(enum bootstage_id id, const char *name, | |
52 | + int flags) | |
53 | +{ | |
54 | + struct bootstage_record *rec; | |
55 | + ulong mark = timer_get_boot_us(); | |
56 | + | |
57 | + if (flags & BOOTSTAGEF_ALLOC) | |
58 | + id = next_id++; | |
59 | + | |
60 | + if (id < BOOTSTAGE_ID_COUNT) { | |
61 | + rec = &record[id]; | |
62 | + | |
63 | + /* Only record the first event for each */ | |
64 | + if (!rec->time_us) { | |
65 | + rec->time_us = mark; | |
66 | + rec->name = name; | |
67 | + rec->flags = flags; | |
68 | + rec->id = id; | |
69 | + } | |
70 | + } | |
71 | + | |
72 | + /* Tell the board about this progress */ | |
73 | + show_boot_progress(flags & BOOTSTAGEF_ERROR ? -id : id); | |
74 | + return mark; | |
75 | +} | |
76 | + | |
77 | + | |
78 | +ulong bootstage_mark(enum bootstage_id id) | |
79 | +{ | |
80 | + return bootstage_add_record(id, NULL, 0); | |
81 | +} | |
82 | + | |
83 | +ulong bootstage_error(enum bootstage_id id) | |
84 | +{ | |
85 | + return bootstage_add_record(id, NULL, BOOTSTAGEF_ERROR); | |
86 | +} | |
87 | + | |
88 | +ulong bootstage_mark_name(enum bootstage_id id, const char *name) | |
89 | +{ | |
90 | + int flags = 0; | |
91 | + | |
92 | + if (id == BOOTSTAGE_ID_ALLOC) | |
93 | + flags = BOOTSTAGEF_ALLOC; | |
94 | + return bootstage_add_record(id, name, flags); | |
95 | +} | |
96 | + | |
97 | +static void print_time(unsigned long us_time) | |
98 | +{ | |
99 | + char str[15], *s; | |
100 | + int grab = 3; | |
101 | + | |
102 | + /* We don't seem to have %'d in U-Boot */ | |
103 | + sprintf(str, "%12lu", us_time); | |
104 | + for (s = str + 3; *s; s += grab) { | |
105 | + if (s != str + 3) | |
106 | + putc(s[-1] != ' ' ? ',' : ' '); | |
107 | + printf("%.*s", grab, s); | |
108 | + grab = 3; | |
109 | + } | |
110 | +} | |
111 | + | |
112 | +static uint32_t print_time_record(enum bootstage_id id, | |
113 | + struct bootstage_record *rec, uint32_t prev) | |
114 | +{ | |
115 | + print_time(rec->time_us); | |
116 | + print_time(rec->time_us - prev); | |
117 | + if (rec->name) | |
118 | + printf(" %s\n", rec->name); | |
119 | + else if (id >= BOOTSTAGE_ID_USER) | |
120 | + printf(" user_%d\n", id - BOOTSTAGE_ID_USER); | |
121 | + else | |
122 | + printf(" id=%d\n", id); | |
123 | + return rec->time_us; | |
124 | +} | |
125 | + | |
126 | +static int h_compare_record(const void *r1, const void *r2) | |
127 | +{ | |
128 | + const struct bootstage_record *rec1 = r1, *rec2 = r2; | |
129 | + | |
130 | + return rec1->time_us > rec2->time_us ? 1 : -1; | |
131 | +} | |
132 | + | |
133 | +void bootstage_report(void) | |
134 | +{ | |
135 | + struct bootstage_record *rec = record; | |
136 | + int id; | |
137 | + uint32_t prev; | |
138 | + | |
139 | + puts("Timer summary in microseconds:\n"); | |
140 | + printf("%11s%11s %s\n", "Mark", "Elapsed", "Stage"); | |
141 | + | |
142 | + /* Fake the first record - we could get it from early boot */ | |
143 | + rec->name = "reset"; | |
144 | + rec->time_us = 0; | |
145 | + prev = print_time_record(BOOTSTAGE_ID_AWAKE, rec, 0); | |
146 | + | |
147 | + /* Sort records by increasing time */ | |
148 | + qsort(record, ARRAY_SIZE(record), sizeof(*rec), h_compare_record); | |
149 | + | |
150 | + for (id = 0; id < BOOTSTAGE_ID_COUNT; id++, rec++) { | |
151 | + if (rec->time_us != 0) | |
152 | + prev = print_time_record(rec->id, rec, prev); | |
153 | + } | |
154 | + if (next_id > BOOTSTAGE_ID_COUNT) | |
155 | + printf("(Overflowed internal boot id table by %d entries\n" | |
156 | + "- please increase CONFIG_BOOTSTAGE_USER_COUNT\n", | |
157 | + next_id - BOOTSTAGE_ID_COUNT); | |
158 | +} |
include/bootstage.h
... | ... | @@ -26,6 +26,11 @@ |
26 | 26 | #ifndef _BOOTSTAGE_H |
27 | 27 | #define _BOOTSTAGE_H |
28 | 28 | |
29 | +/* The number of boot stage records available for the user */ | |
30 | +#ifndef CONFIG_BOOTSTAGE_USER_COUNT | |
31 | +#define CONFIG_BOOTSTAGE_USER_COUNT 20 | |
32 | +#endif | |
33 | + | |
29 | 34 | /* |
30 | 35 | * A list of boot stages that we know about. Each of these indicates the |
31 | 36 | * state that we are at, and the action that we are about to perform. For |
... | ... | @@ -169,6 +174,33 @@ |
169 | 174 | |
170 | 175 | BOOTSTAGE_ID_NAND_FIT_READ = 150, |
171 | 176 | BOOTSTAGE_ID_NAND_FIT_READ_OK, |
177 | + | |
178 | + /* | |
179 | + * These boot stages are new, higher level, and not directly related | |
180 | + * to the old boot progress numbers. They are useful for recording | |
181 | + * rough boot timing information. | |
182 | + */ | |
183 | + BOOTSTAGE_ID_AWAKE, | |
184 | + BOOTSTAGE_ID_START_UBOOT_F, | |
185 | + BOOTSTAGE_ID_START_UBOOT_R, | |
186 | + BOOTSTAGE_ID_USB_START, | |
187 | + BOOTSTAGE_ID_ETH_START, | |
188 | + BOOTSTAGE_ID_BOOTP_START, | |
189 | + BOOTSTAGE_ID_BOOTP_STOP, | |
190 | + BOOTSTAGE_ID_BOOTM_START, | |
191 | + BOOTSTAGE_ID_BOOTM_HANDOFF, | |
192 | + BOOTSTAGE_ID_MAIN_LOOP, | |
193 | + BOOTSTAGE_KERNELREAD_START, | |
194 | + BOOTSTAGE_KERNELREAD_STOP, | |
195 | + | |
196 | + BOOTSTAGE_ID_CPU_AWAKE, | |
197 | + BOOTSTAGE_ID_MAIN_CPU_AWAKE, | |
198 | + BOOTSTAGE_ID_MAIN_CPU_READY, | |
199 | + | |
200 | + /* a few spare for the user, from here */ | |
201 | + BOOTSTAGE_ID_USER, | |
202 | + BOOTSTAGE_ID_COUNT = BOOTSTAGE_ID_USER + CONFIG_BOOTSTAGE_USER_COUNT, | |
203 | + BOOTSTAGE_ID_ALLOC, | |
172 | 204 | }; |
173 | 205 | |
174 | 206 | /* |
... | ... | @@ -189,6 +221,11 @@ |
189 | 221 | |
190 | 222 | ulong bootstage_error(enum bootstage_id id); |
191 | 223 | |
224 | +ulong bootstage_mark_name(enum bootstage_id id, const char *name); | |
225 | + | |
226 | +/* Print a report about boot time */ | |
227 | +void bootstage_report(void); | |
228 | + | |
192 | 229 | #else |
193 | 230 | /* |
194 | 231 | * This is a dummy implementation which just calls show_boot_progress(), |
... | ... | @@ -206,6 +243,12 @@ |
206 | 243 | show_boot_progress(-id); |
207 | 244 | return 0; |
208 | 245 | } |
246 | + | |
247 | +static inline ulong bootstage_mark_name(enum bootstage_id id, const char *name) | |
248 | +{ | |
249 | + return 0; | |
250 | +} | |
251 | + | |
209 | 252 | |
210 | 253 | #endif /* CONFIG_BOOTSTAGE */ |
211 | 254 |