Chromium Code Reviews| Index: common/cmd_time.c |
| diff --git a/common/cmd_time.c b/common/cmd_time.c |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..689b671e45a8842ba55570fcfebe53d09562392f |
| --- /dev/null |
| +++ b/common/cmd_time.c |
| @@ -0,0 +1,134 @@ |
| +/* |
| + * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
| + * Use of this source code is governed by a BSD-style license that can be |
| + * found in the LICENSE file. |
| + * |
| + * Alternatively, this software may be distributed under the terms of the |
| + * GNU General Public License ("GPL") version 2 as published by the Free |
| + * Software Foundation. |
| + */ |
| + |
| +/* time - run a command and report its run time */ |
| + |
| +#include <common.h> |
| +#include <command.h> |
| + |
| +#define ABSDIFF(u, v) ({ \ |
| + const unsigned long int __u = (u), __v = (v); \ |
| + __u > __v ? __u - __v : __v - __u; \ |
| +}) |
| + |
| +static void report_time(unsigned long int cycles) |
| +{ |
| +#ifdef CONFIG_SYS_HZ |
| + unsigned long int minutes, seconds, milliseconds; |
| + unsigned long int total_seconds, remainder, guess, err, err2; |
| + |
| + total_seconds = cycles / CONFIG_SYS_HZ; |
| + minutes = total_seconds / 60; |
| + seconds = total_seconds % 60; |
| + |
| + /* |
| + * We could approximate millisecond value through |
| + * milliseconds = rounding(1000.0 * remainder / CONFIG_SYS_HZ) |
| + * where |
| + * remainder = cycles % CONFIG_SYS_HZ, |
| + * which minimizes the error |
| + * fabs(((float) remainder) / CONFIG_SYS_HZ - milliseconds / 1000.0). |
| + * |
| + * Nevertheless, this approximation, though precise, is undesirable |
| + * for its use of floating-point arithmetic. |
| + * |
| + * So instead we approximate the millisecond value through a 2-step |
| + * method that only uses integer arithmetic. |
| + * |
| + * First, we compute an initial guess |
| + * guess = remainder * 1000 / CONFIG_SYS_HZ. |
| + * |
| + * Then, we adjust the initial guess to form a better guess by |
| + * trying guess - 1 and guess + 1. |
| + * |
| + * My experiment showed that in most cases this 2-step approximation |
| + * is as good as the floating-point approximation method above. |
| + * |
| + * Note: We compute the error using |
| + * abs(remainder * 1000 - milliseconds * CONFIG_SYS_HZ) |
| + * to avoid divisions (and truncation errors). |
| + */ |
| + |
| + /* initial guess */ |
| + remainder = cycles % CONFIG_SYS_HZ; |
| + milliseconds = guess = remainder * 1000 / CONFIG_SYS_HZ; |
|
sjg
2011/04/05 01:21:44
I think it is good enough if you just stop here. D
Che-Liang Chiou
2011/04/06 08:48:40
Thanks. This is better.
|
| + err = ABSDIFF(remainder * 1000, guess * CONFIG_SYS_HZ); |
| + |
| + /* try guess - 1 */ |
| + if (guess > 0) { |
| + err2 = ABSDIFF(remainder * 1000, (guess - 1) * CONFIG_SYS_HZ); |
| + if (err2 < err) { |
| + milliseconds = guess - 1; |
| + err = err2; |
| + } |
| + } |
| + |
| + /* try guess + 1 */ |
| + if (guess < 999) { |
| + err2 = ABSDIFF(remainder * 1000, (guess + 1) * CONFIG_SYS_HZ); |
| + if (err2 < err) { |
| + milliseconds = guess + 1; |
| + err = err2; |
| + } |
| + } |
| + |
| + printf("time:"); |
| + if (minutes) |
| + printf(" %lu minutes and", minutes); |
|
sjg
2011/04/05 01:21:44
It might be easier for a test system to parse if y
Che-Liang Chiou
2011/04/06 08:48:40
Done.
|
| + printf(" %lu.%03lu seconds, or", seconds, milliseconds); |
| + printf(" %lu ticks\n", cycles); |
| +#else |
| + printf("CONFIG_SYS_HZ not defined\n"); |
| + printf("time: %lu ticks\n", cycles); |
| +#endif |
| +} |
| + |
| +int do_time(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
| +{ |
| + const int target_argc = argc - 1; |
| + int retval = 0; |
| + unsigned long int cycles = 0; |
| + cmd_tbl_t *target_cmdtp = NULL; |
| + |
| + if (argc == 1) { |
| + printf("no command provided\n"); |
| + return 128; |
|
sjg
2011/04/05 01:21:44
What is the significance of 128? Is it argument er
Che-Liang Chiou
2011/04/06 08:48:40
I thought that we might be able to know either it
|
| + } |
| + |
| + /* parse command */ |
| + target_cmdtp = find_cmd(argv[1]); |
| + if (!target_cmdtp) { |
| + printf("command not found: %s\n", argv[1]); |
| + return 128; |
| + } |
| + |
| + if (target_argc > target_cmdtp->maxargs) { |
| + printf("maxarags exceeded: %d > %d\n", target_argc, |
| + target_cmdtp->maxargs); |
| + return 128; |
| + } |
| + |
| + /* run the command and report run time */ |
| + cycles = get_timer_masked(); |
| + retval = target_cmdtp->cmd(target_cmdtp, 0, target_argc, argv + 1); |
| + cycles = get_timer_masked() - cycles; |
| + |
| + putc('\n'); |
| + report_time(cycles); |
| + |
| + return retval; |
| +} |
| + |
| +U_BOOT_CMD(time, CONFIG_SYS_MAXARGS, 0, do_time, |
| + "run a command and report its run time", |
| + "command [args...]\n" |
| + "the return value of time is the return value of " |
| + "the command it executed, " |
| + "or 128 if there is an internal error of time.\n"); |