* [PATCH 1/3] debugfs utility routines for perf
2009-11-01 21:55 [PATCH 0/3] perf latency command Clark Williams
@ 2009-11-01 21:56 ` Clark Williams
2009-11-08 17:06 ` [tip:perf/core] perf tools: Add " tip-bot for Clark Williams
2009-11-01 21:57 ` [PATCH 2/3] modify perf routines to use new debugfs routines Clark Williams
2009-11-01 21:58 ` [PATCH 3/3] perf latency builtin command Clark Williams
2 siblings, 1 reply; 9+ messages in thread
From: Clark Williams @ 2009-11-01 21:56 UTC (permalink / raw)
To: Ingo Molnar; +Cc: Peter Zijlstra, Arnaldo Carvalho de Melo, LKML
[-- Attachment #1: Type: text/plain, Size: 6581 bytes --]
Add routines to locate the debugfs mount point and to manage the
mounting and unmountin of the debugfs.
Signed-off-by: Clark Williams <williams@redhat.com>
---
tools/perf/util/debugfs.c | 241 +++++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/debugfs.h | 22 ++++
2 files changed, 263 insertions(+), 0 deletions(-)
create mode 100644 tools/perf/util/debugfs.c
create mode 100644 tools/perf/util/debugfs.h
diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c
new file mode 100644
index 0000000..34f7983
--- /dev/null
+++ b/tools/perf/util/debugfs.c
@@ -0,0 +1,241 @@
+#include "util.h"
+#include "debugfs.h"
+#include "cache.h"
+
+static int debugfs_premounted = 0;
+static char debugfs_mountpoint[MAX_PATH+1];
+
+static const char *debugfs_known_mountpoints[] = {
+ "/sys/kernel/debug/",
+ "/debug/",
+ 0,
+};
+
+/* use this to force a umount */
+void debugfs_force_cleanup(void)
+{
+ debugfs_find_mountpoint();
+ debugfs_premounted = 0;
+ debugfs_umount();
+}
+
+/* construct a full path to a debugfs element */
+int debugfs_make_path(const char *element, char *buffer, int size)
+{
+ int len;
+
+ if (strlen(debugfs_mountpoint) == 0) {
+ buffer[0] = '\0';
+ return -1;
+ }
+
+ len = strlen(debugfs_mountpoint) + strlen(element) + 1;
+ if (len >= size)
+ return len+1;
+
+ snprintf(buffer, size-1, "%s/%s", debugfs_mountpoint, element);
+ return 0;
+}
+
+/* find the path to the mounted debugfs */
+const char *
+debugfs_find_mountpoint(void)
+{
+ static int debugfs_found = 0;
+ char type[100];
+ FILE *fp;
+ const char **ptr;
+
+ if (debugfs_found)
+ return (const char *) debugfs_mountpoint;
+
+ ptr = debugfs_known_mountpoints;
+ while(*ptr) {
+ if (debugfs_valid_mountpoint(*ptr) == 0) {
+ debugfs_found = 1;
+ strcpy(debugfs_mountpoint, *ptr);
+ return debugfs_mountpoint;
+ }
+ ptr++;
+ }
+
+ /* give up and parse /proc/mounts */
+ if ((fp = fopen("/proc/mounts","r")) == NULL)
+ die("Can't open /proc/mounts for read");
+
+ while (fscanf(fp, "%*s %"
+ STR(MAX_PATH)
+ "s %99s %*s %*d %*d\n",
+ debugfs_mountpoint, type) == 2) {
+ if (strcmp(type, "debugfs") == 0)
+ break;
+ }
+ fclose(fp);
+
+ if (strcmp(type, "debugfs") != 0)
+ return NULL;
+
+ debugfs_found = 1;
+
+ return debugfs_mountpoint;
+}
+
+/* verify that a mountpoint is actually a debugfs instance */
+
+int
+debugfs_valid_mountpoint(const char *debugfs)
+{
+ struct statfs st_fs;
+
+ if (statfs(debugfs, &st_fs) < 0)
+ return -ENOENT;
+ else if (st_fs.f_type != (long) DEBUGFS_MAGIC)
+ return -ENOENT;
+ return 0;
+}
+
+
+int
+debugfs_valid_entry(const char *path)
+{
+ struct stat st;
+
+ if (stat(path, &st))
+ return -errno;
+ return 0;
+}
+
+/* mount the debugfs somewhere */
+
+int
+debugfs_mount(const char *mountpoint)
+{
+ char mountcmd[128];
+
+ /* see if it's already mounted */
+ if (debugfs_find_mountpoint()) {
+ debugfs_premounted = 1;
+ return 0;
+ }
+
+ /* if not mounted and no argument */
+ if (mountpoint == NULL) {
+ /* see if environment variable set */
+ mountpoint = getenv(PERF_DEBUGFS_ENVIRONMENT);
+ /* if no environment variable, use default */
+ if (mountpoint == NULL)
+ mountpoint = "/sys/kernel/debug";
+ }
+
+ /* save the mountpoint */
+ strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint));
+
+ /* mount it */
+ snprintf(mountcmd, sizeof(mountcmd),
+ "/bin/mount -t debugfs debugfs %s", mountpoint);
+ return system(mountcmd);
+}
+
+/* umount the debugfs */
+
+int
+debugfs_umount(void)
+{
+ int ret;
+ char umountcmd[128];
+
+ /* if it was already mounted, leave it */
+ if (debugfs_premounted)
+ return 0;
+
+ /* make sure it's a valid mount point */
+ if ((ret = debugfs_valid_mountpoint(debugfs_mountpoint)))
+ return ret;
+
+ snprintf(umountcmd, sizeof(umountcmd),
+ "/bin/umount %s", debugfs_mountpoint);
+ return system(umountcmd);
+}
+
+
+int
+debugfs_write(const char *entry, const char *value)
+{
+ int ret, count;
+ int fd;
+ char path[MAX_PATH+1];
+
+ /* construct the path */
+ snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry);
+
+ /* verify that it exists */
+ if ((ret = debugfs_valid_entry(path)))
+ return ret;
+
+ /* get how many chars we're going to write */
+ count = strlen(value);
+
+ /* open the debugfs entry */
+ if ((fd = open(path, O_RDWR)) < 0)
+ return -errno;
+
+ while (count > 0) {
+ /* write it */
+ ret = write(fd, value, count);
+ if (ret <= 0) {
+ if (ret == EAGAIN)
+ continue;
+ close(fd);
+ return -errno;
+ }
+ count -= ret;
+ }
+
+ /* close it */
+ close(fd);
+
+ /* return success */
+ return 0;
+}
+
+/*
+ * read a debugfs entry
+ * returns the number of chars read or a negative errno
+ */
+
+int
+debugfs_read(const char *entry, char *buffer, size_t size)
+{
+ int ret;
+ int fd;
+ char path[MAX_PATH+1];
+
+ /* construct the path */
+ snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry);
+
+ /* verify that it exists */
+ if ((ret = debugfs_valid_entry(path)))
+ return ret;
+
+ /* open the debugfs entry */
+ if ((fd = open(path, O_RDONLY)) < 0)
+ return -errno;
+
+ do {
+ /* read it */
+ ret = read(fd, buffer, size);
+ if (ret == 0) {
+ close(fd);
+ return EOF;
+ }
+ } while (ret < 0 && errno == EAGAIN);
+
+ /* close it */
+ close(fd);
+
+ /* make *sure* there's a null character at the end */
+ buffer[ret] = '\0';
+
+ /* return the number of chars read */
+ return ret;
+}
diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h
new file mode 100644
index 0000000..df14c2a
--- /dev/null
+++ b/tools/perf/util/debugfs.h
@@ -0,0 +1,22 @@
+#ifndef __DEBUGFS_H__
+#define __DEBUGFS_H__
+#include <sys/mount.h>
+#ifndef MAX_PATH
+#define MAX_PATH 256
+#endif
+
+#ifndef STR
+#define _STR(x) #x
+#define STR(x) _STR(x)
+#endif
+
+extern const char * debugfs_find_mountpoint(void);
+extern int debugfs_valid_mountpoint(const char *debugfs);
+extern int debugfs_valid_entry(const char *path);
+extern int debugfs_mount(const char *mountpoint);
+extern int debugfs_umount(void);
+extern int debugfs_write(const char *entry, const char *value);
+extern int debugfs_read(const char *entry, char *buffer, size_t size);
+extern void debugfs_force_cleanup(void);
+extern int debugfs_make_path(const char *element, char *buffer, int size);
+#endif
--
1.6.2.5
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 2/3] modify perf routines to use new debugfs routines
2009-11-01 21:55 [PATCH 0/3] perf latency command Clark Williams
2009-11-01 21:56 ` [PATCH 1/3] debugfs utility routines for perf Clark Williams
@ 2009-11-01 21:57 ` Clark Williams
2009-11-08 17:06 ` [tip:perf/core] perf tools: Modify " tip-bot for Clark Williams
2009-11-01 21:58 ` [PATCH 3/3] perf latency builtin command Clark Williams
2 siblings, 1 reply; 9+ messages in thread
From: Clark Williams @ 2009-11-01 21:57 UTC (permalink / raw)
To: Ingo Molnar; +Cc: Peter Zijlstra, Arnaldo Carvalho de Melo, LKML
[-- Attachment #1: Type: text/plain, Size: 3897 bytes --]
modified perf.c get_debugfs_mntpnt() to use the util/debugfs.c
debugfs_find_mountpoint()
modified util/parse-events.c to use debugfs_valid_mountpoint().
Signed-off-by: Clark Williams <williams@redhat.com>
---
tools/perf/perf.c | 45
++++++--------------------------------- tools/perf/util/parse-events.c
| 17 +++----------- 2 files changed, 11 insertions(+), 51 deletions(-)
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 9cafe54..940cbf6 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -14,6 +14,7 @@
#include "util/run-command.h"
#include "util/parse-events.h"
#include "util/string.h"
+#include "util/debugfs.h"
const char perf_usage_string[] =
"perf [--version] [--help] COMMAND [ARGS]";
@@ -296,6 +297,7 @@ static void handle_internal_command(int argc, const
char **argv) { "trace", cmd_trace, 0 },
{ "sched", cmd_sched, 0 },
{ "probe", cmd_probe, 0 },
+ { "latency", cmd_latency, 0},
};
unsigned int i;
static const char ext[] = STRIP_EXTENSION;
@@ -383,45 +385,12 @@ static int run_argv(int *argcp, const char
***argv) /* mini /proc/mounts parser: searching for "^blah /mount/point
debugfs" */ static void get_debugfs_mntpt(void)
{
- FILE *file;
- char fs_type[100];
- char debugfs[MAXPATHLEN];
+ const char *path = debugfs_find_mountpoint();
- /*
- * try the standard location
- */
- if (valid_debugfs_mount("/sys/kernel/debug/") == 0) {
- strcpy(debugfs_mntpt, "/sys/kernel/debug/");
- return;
- }
-
- /*
- * try the sane location
- */
- if (valid_debugfs_mount("/debug/") == 0) {
- strcpy(debugfs_mntpt, "/debug/");
- return;
- }
-
- /*
- * give up and parse /proc/mounts
- */
- file = fopen("/proc/mounts", "r");
- if (file == NULL)
- return;
-
- while (fscanf(file, "%*s %"
- STR(MAXPATHLEN)
- "s %99s %*s %*d %*d\n",
- debugfs, fs_type) == 2) {
- if (strcmp(fs_type, "debugfs") == 0)
- break;
- }
- fclose(file);
- if (strcmp(fs_type, "debugfs") == 0) {
- strncpy(debugfs_mntpt, debugfs, MAXPATHLEN);
- debugfs_mntpt[MAXPATHLEN - 1] = '\0';
- }
+ if (path)
+ strncpy(debugfs_mntpt, path, sizeof(debugfs_mntpt));
+ else
+ debugfs_mntpt[0] = '\0';
}
int main(int argc, const char **argv)
diff --git a/tools/perf/util/parse-events.c
b/tools/perf/util/parse-events.c index 31baa5a..097938a 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -7,6 +7,7 @@
#include "string.h"
#include "cache.h"
#include "header.h"
+#include "debugfs.h"
int nr_counters;
@@ -149,16 +150,6 @@ static int tp_event_has_id(struct dirent *sys_dir,
struct dirent *evt_dir)
#define MAX_EVENT_LENGTH 512
-int valid_debugfs_mount(const char *debugfs)
-{
- struct statfs st_fs;
-
- if (statfs(debugfs, &st_fs) < 0)
- return -ENOENT;
- else if (st_fs.f_type != (long) DEBUGFS_MAGIC)
- return -ENOENT;
- return 0;
-}
struct tracepoint_path *tracepoint_id_to_path(u64 config)
{
@@ -171,7 +162,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64
config) char evt_path[MAXPATHLEN];
char dir_path[MAXPATHLEN];
- if (valid_debugfs_mount(debugfs_path))
+ if (debugfs_valid_mountpoint(debugfs_path))
return NULL;
sys_dir = opendir(debugfs_path);
@@ -510,7 +501,7 @@ static enum event_result
parse_tracepoint_event(const char **strp, char
sys_name[MAX_EVENT_LENGTH]; unsigned int sys_length, evt_length;
- if (valid_debugfs_mount(debugfs_path))
+ if (debugfs_valid_mountpoint(debugfs_path))
return 0;
evt_name = strchr(*strp, ':');
@@ -788,7 +779,7 @@ static void print_tracepoint_events(void)
char evt_path[MAXPATHLEN];
char dir_path[MAXPATHLEN];
- if (valid_debugfs_mount(debugfs_path))
+ if (debugfs_valid_mountpoint(debugfs_path))
return;
sys_dir = opendir(debugfs_path);
--
1.6.2.5
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 3/3] perf latency builtin command
2009-11-01 21:55 [PATCH 0/3] perf latency command Clark Williams
2009-11-01 21:56 ` [PATCH 1/3] debugfs utility routines for perf Clark Williams
2009-11-01 21:57 ` [PATCH 2/3] modify perf routines to use new debugfs routines Clark Williams
@ 2009-11-01 21:58 ` Clark Williams
2009-11-03 19:28 ` Ingo Molnar
2 siblings, 1 reply; 9+ messages in thread
From: Clark Williams @ 2009-11-01 21:58 UTC (permalink / raw)
To: Ingo Molnar; +Cc: Peter Zijlstra, Arnaldo Carvalho de Melo, LKML
[-- Attachment #1: Type: text/plain, Size: 15367 bytes --]
This is the first cut at a 'perf latency' command to manage the
hwlat_detector kernel module, used to detect hardware induced
latencies (e.g. SMIs).
Signed-off-by: Clark Williams <williams@redhat.com>
---
tools/perf/Documentation/perf-latency.txt | 64 +++++
tools/perf/Documentation/perf.txt | 2 +-
tools/perf/Makefile | 3 +
tools/perf/builtin-latency.c | 383
+++++++++++++++++++++++++++++ tools/perf/builtin.h
| 2 +- tools/perf/command-list.txt | 1 +
6 files changed, 453 insertions(+), 2 deletions(-)
create mode 100644 tools/perf/Documentation/perf-latency.txt
create mode 100644 tools/perf/builtin-latency.c
diff --git a/tools/perf/Documentation/perf-latency.txt
b/tools/perf/Documentation/perf-latency.txt new file mode 100644
index 0000000..f615d08
--- /dev/null
+++ b/tools/perf/Documentation/perf-latency.txt
@@ -0,0 +1,64 @@
+perf-latency(1)
+===============
+
+NAME
+----
+perf-latency - check for hardware latencies
+
+SYNOPSIS
+--------
+[verse]
+'per latency' [OPTIONS]
+
+DESCRIPTION
+-----------
+This command manages the hwlat_detector kernel module, which is used
+to test the system for hardware induced latencies. The command runs
+for a specified amount of time (default: 60 seconds) and samples the
+system Time Stamp Counter (TSC) register, looking for gaps which
+exceed a threshold. If a gap exceeding the threshold is seen, a
+timestamp and the gap (in microseconds) is printed to the standard
+output.
+
+OPTIONS
+-------
+--duration=<n>{s,m,h,d,w}::
+ The length of time the test should run. (default: 60 seconds)
+
+--window=<n>{us,ms,s,m}::
+ The sample period for the test. (default 1 second)
+
+--width==<n>{us,ms,s,m}::
+ The test time within the sample window. (default 500
+ milliseconds)
+
+--threshold==<n>{us,ms,s}::
+ Threshold above which is considered a latency. (default
50 microseconds) +
+--cleanup::
+ Force unload of hwlat_detector module and umount of debugfs.
+
+--debug::
+ Turn on lots of debugging prints
+
+
+Time values are specified as a decimal integer value with an optional
+unit suffix
+
+ us - microseconds
+ ms - milliseconds
+ s - seconds
+ m - minutes
+ h - hours
+ d - days
+ w - weeks
+
+EXAMPLES
+--------
+
+$ perf latency --duration=1h --width=750ms --threshold=10us
+
+This invocation runs the latency detector for 1 hour, using the
+default window size of 1 second, a width of 750 milliseconds and a
+threshold of 10 microseconds. Any gap larger than 10 microseconds
+detected during sampling will be printed to the screen.
diff --git a/tools/perf/Documentation/perf.txt
b/tools/perf/Documentation/perf.txt index 69c8325..358856e 100644
--- a/tools/perf/Documentation/perf.txt
+++ b/tools/perf/Documentation/perf.txt
@@ -21,4 +21,4 @@ SEE ALSO
--------
linkperf:perf-stat[1], linkperf:perf-top[1],
linkperf:perf-record[1], linkperf:perf-report[1],
-linkperf:perf-list[1]
+linkperf:perf-list[1], linkperf:perf-latency[1]
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 800783d..68f210a 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -374,6 +374,7 @@ LIB_H += util/sort.h
LIB_H += util/hist.h
LIB_H += util/thread.h
LIB_H += util/data_map.h
+LIB_H += util/debugfs.h
LIB_OBJS += util/abspath.o
LIB_OBJS += util/alias.o
@@ -415,6 +416,7 @@ LIB_OBJS += util/svghelper.o
LIB_OBJS += util/sort.o
LIB_OBJS += util/hist.o
LIB_OBJS += util/data_map.o
+LIB_OBJS += util/debugfs.o
BUILTIN_OBJS += builtin-annotate.o
BUILTIN_OBJS += builtin-help.o
@@ -427,6 +429,7 @@ BUILTIN_OBJS += builtin-timechart.o
BUILTIN_OBJS += builtin-top.o
BUILTIN_OBJS += builtin-trace.o
BUILTIN_OBJS += builtin-probe.o
+BUILTIN_OBJS += builtin-latency.o
PERFLIBS = $(LIB_FILE)
diff --git a/tools/perf/builtin-latency.c b/tools/perf/builtin-latency.c
new file mode 100644
index 0000000..7a33f71
--- /dev/null
+++ b/tools/perf/builtin-latency.c
@@ -0,0 +1,383 @@
+#include "builtin.h"
+#include "perf.h"
+
+#include "util/util.h"
+#include "util/parse-options.h"
+#include "util/debugfs.h"
+
+static struct latency_params {
+ long duration;
+ long threshold;
+ long window;
+ long width;
+ int cleanup;
+ int debugging;
+} parameters = {
+ .duration = 0,
+ .threshold = -1,
+ .window = -1,
+ .width = -1,
+ .cleanup = 0,
+ .debugging = 0,
+};
+
+/* strings for argument processing */
+#define DEFAULT_DURATION "60s" /* 60 seconds */
+
+static char *duration_str = (char*)DEFAULT_DURATION;
+static char *threshold_str = (char*)"-1";
+static char *window_str = (char*)"-1";
+static char *width_str = (char*)"-1";
+
+#define SAMPLE_POLL_INTERVAL 1 /* poll for sample every second */
+#define ENABLE_RETRIES 100 /* how many times to retry
enable/disable */ +
+static int hwlat_preloaded;
+
+static void debug(const char *fmt, ...)
+{
+ if (parameters.debugging) {
+ va_list ap;
+ fputs("PERF DEBUG: ", stderr);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+}
+
+static int check_module_loaded(const char *modname)
+{
+ char line[256], module[64];
+ FILE *fp = fopen("/proc/modules", "r");
+ int len;
+
+ if (fp == NULL)
+ return -errno;
+ len = strlen(modname);
+ while (fgets(line, sizeof(line) - 1, fp)) {
+ if (sscanf(line, "%s", module) == EOF)
+ continue;
+ if (strncmp(module, modname, len) == 0) {
+ fclose(fp);
+ return 0;
+ }
+ }
+ fclose(fp);
+ return -1;
+}
+
+static int
+hwlat_load_module(void)
+{
+ if (hwlat_preloaded)
+ return 0;
+ debug("loading hwlat_detector module\n");
+ if (check_module_loaded("hwlat_detector") == 0) {
+ debug("hwlat_detector already loaded\n");
+ hwlat_preloaded = 1;
+ return 0;
+ }
+ return system("/sbin/modprobe hwlat_detector");
+}
+
+static int
+hwlat_unload_module(void)
+{
+ if (hwlat_preloaded)
+ return 0;
+ if (check_module_loaded("hwlat_detector"))
+ return 0;
+ return system("/sbin/modprobe -r hwlat_detector");
+}
+
+static int
+hwlat_get_value(const char *entry)
+{
+ char hwlat_entry[128];
+ char buffer[256];
+ int nread, val;
+
+ snprintf(hwlat_entry, sizeof(hwlat_entry),
"hwlat_detector/%s", entry);
+ nread = debugfs_read(hwlat_entry, buffer, sizeof(buffer));
+ if (nread < 0)
+ return nread;
+ val = strtol(buffer, NULL, 10);
+ debug("read %d from %s\n", val, hwlat_entry);
+ return val;
+}
+
+static int
+hwlat_put_value(const char *entry, int value)
+{
+ char hwlat_entry[128];
+ char buffer[256];
+
+ snprintf(hwlat_entry, sizeof(hwlat_entry),
"hwlat_detector/%s", entry);
+ snprintf(buffer, sizeof(buffer), "%d", value);
+ debug("writing %s to %s\n", buffer, hwlat_entry);
+ return debugfs_write(hwlat_entry, buffer);
+}
+
+/*
+ * read the sample file and if we get something return it
+ * the file is opened non-blocking so handle EAGAIN
+ */
+static int
+hwlat_read_sample(int fd, char *buffer, size_t size)
+{
+ int ret;
+ ret = read(fd, buffer, size);
+ if (ret >= 0)
+ buffer[ret] = '\0';
+ /* don't error if there's nothing to read */
+ if (ret < 0 && errno == EAGAIN)
+ return 0;
+ return ret;
+}
+
+static int
+hwlat_enable(void)
+{
+ int retries = ENABLE_RETRIES;
+ int val;
+ do {
+ hwlat_put_value("enable", 1);
+
+ } while ((val = hwlat_get_value("enable")) != 1 && --retries >
0);
+ if (val != 1)
+ return -1;
+ return 0;
+}
+
+static int
+hwlat_disable(void)
+{
+ int val;
+ int retries = ENABLE_RETRIES;
+
+ do {
+ hwlat_put_value("enable", 0);
+ } while ((val = hwlat_get_value("enable")) != 0 && --retries >
0);
+ if (val != 0)
+ return -1;
+ return 0;
+}
+
+/* perf latency options */
+
+static const char * const latency_usage[] = {
+ "perf latency [<options>]",
+ NULL
+};
+
+static const struct option options[] = {
+ OPT_STRING('d', "duration", &duration_str, "n{s,m,h,d,w}",
+ "total time to test for hardware latencies:
<N>{smhd}"),
+ OPT_STRING('t', "threshold", &threshold_str, "n{ms,s,m,h}",
+ "microsecond value above which is considered a
hardware latency"),
+ OPT_STRING('w', "window", &window_str, "n{ms,s,m,h}",
+ "sample window duration"),
+ OPT_STRING('W', "width", &width_str, "n{ms,s,m,h}",
+ "actual measurment time period (must be <=
window)"),
+ OPT_BOOLEAN('c', "cleanup", ¶meters.cleanup,
+ "force unload of module and umount of debugfs"),
+ OPT_BOOLEAN('D', "debug", ¶meters.debugging, "turn on
debugging prints"),
+ OPT_END()
+};
+
+static int
+ts_less_than(const struct timespec t1, const struct timespec t2)
+{
+ long long diff;
+ diff = NSEC_PER_SEC * (long long)((int) t1.tv_sec - (int)
t2.tv_sec);
+ diff += ((int) t1.tv_nsec - (int) t2.tv_nsec);
+ /*debug("diff: %ld\n", diff);*/
+ if (diff < 0)
+ return 1;
+ return 0;
+}
+
+static long
+str_to_us(const char *str)
+{
+ char *ptr;
+ int val = strtol(str, &ptr, 10);
+ if (ptr) {
+ /* check for milliseconds, seconds, minutes and hours
*/
+ /* ignore anything else */
+ if (strcmp(ptr, "ms") == 0)
+ val *= 1000;
+ else if (strcmp(ptr, "s") == 0)
+ val *= 1000000;
+ else if (strcmp(ptr, "m") == 0)
+ val *= 60000000; /* 1000000 * 60 */
+ else if (strcmp(ptr, "h") == 0)
+ val *= 3600000000; /* 1000000 * 60 * 60 */
+ }
+ return val;
+}
+
+static long
+str_to_sec(const char *str)
+{
+ char *ptr;
+ int val = strtol(str, &ptr, 10);
+
+ if (ptr) {
+ /* check for minutes, hours, days, and weeks */
+ if (strcmp(ptr, "m") == 0)
+ val *= 60;
+ else if (strcmp(ptr, "h") == 0)
+ val *= 360; /* 60 * 60 */
+ else if (strcmp(ptr, "d") == 0)
+ val *= 86400; /* 60 * 60 * 24 */
+ else if (strcmp(ptr, "w") == 0)
+ val *= 604800; /* 60 * 60 * 24 * 7 */
+ }
+ return val;
+}
+
+
+/* main function for hwlat_detector operation */
+static void
+detect (struct latency_params *params)
+{
+ int ret;
+ int fd;
+ char buffer[128];
+ char sample_path[256];
+ struct timespec start, stop, now;
+
+ /* set the parameters */
+ debug("setting window to %d\n", params->window);
+ if ((ret = hwlat_put_value("window", params->window)))
+ die("error writing hwlat_detector 'window' parameter
(%ld): %s",
+ params->window, strerror(errno));
+
+ debug("setting width to %d\n", params->width);
+ if ((ret = hwlat_put_value("width", params->width)))
+ die("error writing hwlat_detector 'width' parameter
(%ld): %s",
+ params->width, strerror(errno));
+
+ debug("setting threshold to %d\n", params->threshold);
+ if ((ret = hwlat_put_value("threshold", params->threshold)))
+ die("error writing hwlat_detector 'threshold'
parameter (%ld): %s",
+ params->threshold, strerror(errno));
+
+ /* open the sample entry */
+ if (debugfs_make_path("hwlat_detector/sample", sample_path,
sizeof(sample_path)))
+ die ("can't get path to sample element: %s\n",
strerror(errno)); +
+ if ((fd = open(sample_path, O_RDONLY|O_NDELAY)) == -1)
+ die("can't open %s: %s\n", sample_path,
strerror(errno)); +
+ /* get our start time */
+ if ((ret = clock_gettime(CLOCK_MONOTONIC, &start)))
+ die ("error getting start time: %s\n", strerror(-ret));
+
+ /* setup stop time */
+ stop = start;
+ stop.tv_sec += params->duration;
+
+ /* start the kmod sampling */
+ if (hwlat_enable())
+ die ("can't enable the hwlat_detector!\n");
+
+ /* read samples until duration is done */
+ if ((ret = clock_gettime(CLOCK_MONOTONIC, &now)))
+ die ("error getting initial time: %s\n",
strerror(-errno)); +
+ while(ts_less_than(now, stop)) {
+ struct timespec ts;
+ debug("checking for sample\n");
+ if ((ret = hwlat_read_sample(fd, buffer,
sizeof(buffer))) < 0)
+ die("error reading sample: %s (%d)",
strerror(errno), errno);
+ /* for now, just print it */
+ if (ret) {
+ buffer[ret-1] = '\0'; /* nuke the newline */
+ puts(buffer);
+ }
+ ts.tv_sec = SAMPLE_POLL_INTERVAL;
+ ts.tv_nsec = 0;
+ clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
+ if ((ret = clock_gettime(CLOCK_MONOTONIC, &now)))
+ die("error getting current time: %s\n",
strerror(-errno));
+ }
+
+ /* stop the kmod sampling */
+ if (hwlat_disable())
+ die ("can't stop the hwlat_detector!\n");
+
+ /* close the sample entry */
+ close(fd);
+}
+
+static void
+sig_cleanup(int signo __used)
+{
+ hwlat_disable();
+ hwlat_unload_module();
+ debugfs_umount();
+}
+
+int
+cmd_latency(int argc, const char **argv, const char *prefix __used)
+{
+ argc = parse_options(argc, argv, options, latency_usage, 0);
+ if (argc)
+ usage_with_options(latency_usage, options);
+
+ if (parameters.cleanup) {
+ printf("cleaning up\n");
+ debugfs_force_cleanup();
+ hwlat_unload_module();
+ return 0;
+ }
+
+ /* setup parameters */
+ parameters.duration = str_to_sec(duration_str);
+ parameters.threshold = str_to_us(threshold_str);
+ parameters.window = str_to_us(window_str);
+ parameters.width = str_to_us(width_str);
+
+ /* mount the debugfs */
+ if (debugfs_mount(NULL))
+ die ("error mounting debugfs: %s\n", strerror(errno));
+
+ /* load the hwlat_detector module */
+ if (hwlat_load_module())
+ die("error loading hwlat_detector kernel module:
%s\n",strerror(errno)); +
+ /* get defaults if not specified on cmdline */
+ if (parameters.window == -1)
+ parameters.window = hwlat_get_value("window");
+ if (parameters.width == -1)
+ parameters.width = hwlat_get_value("width");
+ if (parameters.threshold == -1)
+ parameters.threshold = hwlat_get_value("threshold");
+
+ /* sanity check */
+ if (parameters.width > parameters.window)
+ die ("invalid values for window/width (%ld/%ld)
(window must be larger)\n",
+ parameters.window, parameters.width);
+
+ debug("window: %dus\n", parameters.window);
+ debug("width: %dus\n", parameters.width);
+ debug("threshold: %dus\n", parameters.threshold);
+ debug("duration: %ds\n", parameters.duration);
+ debug("cleanup: %s\n", parameters.cleanup ? "true" :
"false");
+ debug("debug: %s\n", parameters.debugging ? "true" :
"false"); +
+ signal(SIGINT, sig_cleanup);
+ signal(SIGABRT, sig_cleanup);
+
+ /* do detection */
+ detect(¶meters);
+
+ /* clean up */
+ hwlat_unload_module();
+ debugfs_umount();
+
+ return 0;
+}
+
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
index ad5f0f4..6d6c2f1 100644
--- a/tools/perf/builtin.h
+++ b/tools/perf/builtin.h
@@ -26,5 +26,5 @@ extern int cmd_top(int argc, const char **argv, const
char *prefix); extern int cmd_trace(int argc, const char **argv, const
char *prefix); extern int cmd_version(int argc, const char **argv,
const char *prefix); extern int cmd_probe(int argc, const char **argv,
const char *prefix); -
+extern int cmd_latency(int argc, const char **argv, const char
*prefix); #endif
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt
index 6475db4..2063e37 100644
--- a/tools/perf/command-list.txt
+++ b/tools/perf/command-list.txt
@@ -12,3 +12,4 @@ perf-timechart mainporcelain
common perf-top mainporcelain common
perf-trace mainporcelain common
perf-probe mainporcelain common
+perf-latency mainporcelain common
--
1.6.2.5
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply related [flat|nested] 9+ messages in thread