diff mbox

[v9,3/7] trace: Provide a detailed event control interface

Message ID 20130110192313.29862.57905.stgit@fimbulvetr.bsc.es
State New
Headers show

Commit Message

Lluís Vilanova Jan. 10, 2013, 7:23 p.m. UTC
This interface decouples event obtaining from interaction.

Events can be obtained through three different methods:

* identifier
* name
* simple wildcard pattern

Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu>
---
 docs/tracing.txt         |   44 ++++-------
 trace/control-internal.h |   67 ++++++++++++++++
 trace/control.c          |  106 ++++++++++++++++++++++---
 trace/control.h          |  192 +++++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 351 insertions(+), 58 deletions(-)
 create mode 100644 trace/control-internal.h

Comments

Stefan Hajnoczi Jan. 22, 2013, 5:01 p.m. UTC | #1
On Thu, Jan 10, 2013 at 08:23:13PM +0100, Lluís Vilanova wrote:
> diff --git a/trace/control-internal.h b/trace/control-internal.h
> new file mode 100644
> index 0000000..188253a
> --- /dev/null
> +++ b/trace/control-internal.h
> @@ -0,0 +1,67 @@
> +/*
> + * Interface for configuring and controlling the state of tracing events.
> + *
> + * Copyright (C) 2011-2012 Lluís Vilanova <vilanova@ac.upc.edu>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */

Please add an include guard.

> +static inline bool trace_event_is_pattern(const char *str)
> +{
> +    assert(str != NULL);
> +
> +    while (*str != '\0') {
> +        if (*str == '*') {
> +            return true;
> +        }
> +        str++;
> +    }
> +    return false;

Equivalent to:

return strchr(str, '*');

> +static bool glob(const char *pat, const char *ev)

Name collision with glob(3).  Please choose a different name so readers
know we are not calling the POSIX function.

> +/**
> + * trace_print_events:
> + *
> + * Print the state of all events.
> + *
> + * Warning: This function must be implemented by each tracing backend.
> + *
> + * TODO: Should this be moved to generic code?

This is generic code so the TODO can be removed?
Lluís Vilanova Jan. 23, 2013, 5:11 p.m. UTC | #2
Stefan Hajnoczi writes:

> On Thu, Jan 10, 2013 at 08:23:13PM +0100, Lluís Vilanova wrote:
>> diff --git a/trace/control-internal.h b/trace/control-internal.h
>> new file mode 100644
>> index 0000000..188253a
>> --- /dev/null
>> +++ b/trace/control-internal.h
>> @@ -0,0 +1,67 @@
>> +/*
>> + * Interface for configuring and controlling the state of tracing events.
>> + *
>> + * Copyright (C) 2011-2012 Lluís Vilanova <vilanova@ac.upc.edu>
>> + *
>> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
>> + * See the COPYING file in the top-level directory.
>> + */

> Please add an include guard.

It is only meant to be included from "trace/control.h", and only for the sake of
maintaining declarations separated from inlined definitions.



>> +static inline bool trace_event_is_pattern(const char *str)
>> +{
>> +    assert(str != NULL);
>> +
>> +    while (*str != '\0') {
>> +        if (*str == '*') {
>> +            return true;
>> +        }
>> +        str++;
>> +    }
>> +    return false;

> Equivalent to:

> return strchr(str, '*');

Right.



>> +static bool glob(const char *pat, const char *ev)

> Name collision with glob(3).  Please choose a different name so readers
> know we are not calling the POSIX function.

Right, I'll use 'pattern_glob'.



>> +/**
>> + * trace_print_events:
>> + *
>> + * Print the state of all events.
>> + *
>> + * Warning: This function must be implemented by each tracing backend.
>> + *
>> + * TODO: Should this be moved to generic code?

> This is generic code so the TODO can be removed?

No, I meant the opposite. That maybe this should *not* be in the generic control
interface, but I don't know where it should be moved to then, as it's used in
the simple, default and stderr backends, as well as in the monitor.



Thanks,
  Lluis
Stefan Hajnoczi Jan. 24, 2013, 9:35 a.m. UTC | #3
On Wed, Jan 23, 2013 at 06:11:19PM +0100, Lluís Vilanova wrote:
> Stefan Hajnoczi writes:
> 
> > On Thu, Jan 10, 2013 at 08:23:13PM +0100, Lluís Vilanova wrote:
> >> diff --git a/trace/control-internal.h b/trace/control-internal.h
> >> new file mode 100644
> >> index 0000000..188253a
> >> --- /dev/null
> >> +++ b/trace/control-internal.h
> >> @@ -0,0 +1,67 @@
> >> +/*
> >> + * Interface for configuring and controlling the state of tracing events.
> >> + *
> >> + * Copyright (C) 2011-2012 Lluís Vilanova <vilanova@ac.upc.edu>
> >> + *
> >> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> >> + * See the COPYING file in the top-level directory.
> >> + */
> 
> > Please add an include guard.
> 
> It is only meant to be included from "trace/control.h", and only for the sake of
> maintaining declarations separated from inlined definitions.

For sanity I'd still add an include guard.

> >> +/**
> >> + * trace_print_events:
> >> + *
> >> + * Print the state of all events.
> >> + *
> >> + * Warning: This function must be implemented by each tracing backend.
> >> + *
> >> + * TODO: Should this be moved to generic code?
> 
> > This is generic code so the TODO can be removed?
> 
> No, I meant the opposite. That maybe this should *not* be in the generic control
> interface, but I don't know where it should be moved to then, as it's used in
> the simple, default and stderr backends, as well as in the monitor.

I see.  For now I think this is okay and the comment can be dropped.

Stefan
diff mbox

Patch

diff --git a/docs/tracing.txt b/docs/tracing.txt
index 453cc4a..58c61af 100644
--- a/docs/tracing.txt
+++ b/docs/tracing.txt
@@ -100,49 +100,37 @@  respectively.  This ensures portability between 32- and 64-bit platforms.
 
 == Generic interface and monitor commands ==
 
-You can programmatically query and control the dynamic state of trace events
-through a backend-agnostic interface:
+You can programmatically query and control the state of trace events through a
+backend-agnostic interface provided by the header "trace/control.h".
 
-* trace_print_events
+Note that some of the backends do not provide an implementation for some parts
+of this interface, in which case QEMU will just print a warning (please refer to
+header "trace/control.h" to see which routines are backend-dependent).
 
-* trace_event_set_state
-  Enables or disables trace events at runtime inside QEMU.
-  The function returns "true" if the state of the event has been successfully
-  changed, or "false" otherwise:
-
-    #include "trace/control.h"
-    
-    trace_event_set_state("virtio_irq", true); /* enable */
-    [...]
-    trace_event_set_state("virtio_irq", false); /* disable */
-
-Note that some of the backends do not provide an implementation for this
-interface, in which case QEMU will just print a warning.
-
-This functionality is also provided through monitor commands:
+The state of events can also be queried and modified through monitor commands:
 
 * info trace-events
   View available trace events and their state.  State 1 means enabled, state 0
   means disabled.
 
 * trace-event NAME on|off
-  Enable/disable a given trace event or a group of events having common prefix
-  through wildcard.
+  Enable/disable a given trace event or a group of events (using wildcards).
 
 The "-trace events=<file>" command line argument can be used to enable the
 events listed in <file> from the very beginning of the program. This file must
 contain one event name per line.
 
-A basic wildcard matching is supported in both the monitor command "trace
--event" and the events list file. That means you can enable/disable the events
-having a common prefix in a batch. For example, virtio-blk trace events could
-be enabled using:
-  trace-event virtio_blk_* on
-
 If a line in the "-trace events=<file>" file begins with a '-', the trace event
 will be disabled instead of enabled.  This is useful when a wildcard was used
 to enable an entire family of events but one noisy event needs to be disabled.
 
+Wildcard matching is supported in both the monitor command "trace-event" and the
+events list file. That means you can enable/disable the events having a common
+prefix in a batch. For example, virtio-blk trace events could be enabled using
+the following monitor command:
+
+    trace-event virtio_blk_* on
+
 == Trace backends ==
 
 The "tracetool" script automates tedious trace event code generation and also
@@ -263,3 +251,7 @@  guard such computations and avoid its compilation when the event is disabled:
         }
         return ptr;
     }
+
+You can check both if the event has been disabled and is dynamically enabled at
+the same time using the 'trace_event_get_state' routine (see header
+"trace/control.h" for more information).
diff --git a/trace/control-internal.h b/trace/control-internal.h
new file mode 100644
index 0000000..188253a
--- /dev/null
+++ b/trace/control-internal.h
@@ -0,0 +1,67 @@ 
+/*
+ * Interface for configuring and controlling the state of tracing events.
+ *
+ * Copyright (C) 2011-2012 Lluís Vilanova <vilanova@ac.upc.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+
+extern TraceEvent trace_events[];
+
+
+static inline TraceEvent *trace_event_id(TraceEventID id)
+{
+    assert(id < trace_event_count());
+    return &trace_events[id];
+}
+
+static inline TraceEventID trace_event_count(void)
+{
+    return TRACE_EVENT_COUNT;
+}
+
+static inline bool trace_event_is_pattern(const char *str)
+{
+    assert(str != NULL);
+
+    while (*str != '\0') {
+        if (*str == '*') {
+            return true;
+        }
+        str++;
+    }
+    return false;
+}
+
+static inline TraceEventID trace_event_get_id(TraceEvent *ev)
+{
+    assert(ev != NULL);
+    return ev->id;
+}
+
+static inline const char * trace_event_get_name(TraceEvent *ev)
+{
+    assert(ev != NULL);
+    return ev->name;
+}
+
+static inline bool trace_event_get_state_static(TraceEvent *ev)
+{
+    assert(ev != NULL);
+    return ev->sstate;
+}
+
+static inline bool trace_event_get_state_dynamic(TraceEvent *ev)
+{
+    assert(ev != NULL);
+    return ev->dstate;
+}
+
+static inline void trace_event_set_state_dynamic(TraceEvent *ev, bool state)
+{
+    assert(ev != NULL);
+    assert(trace_event_get_state_static(ev));
+    return trace_event_set_state_dynamic_backend(ev, state);
+}
diff --git a/trace/control.c b/trace/control.c
index be05efb..7caaec5 100644
--- a/trace/control.c
+++ b/trace/control.c
@@ -1,19 +1,86 @@ 
 /*
  * Interface for configuring and controlling the state of tracing events.
  *
- * Copyright (C) 2011 Lluís Vilanova <vilanova@ac.upc.edu>
+ * Copyright (C) 2011-2012 Lluís Vilanova <vilanova@ac.upc.edu>
  *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
  */
 
 #include "trace/control.h"
 
 
-void trace_backend_init_events(const char *fname)
+TraceEvent *trace_event_name(const char *name)
+{
+    assert(name != NULL);
+
+    TraceEventID i;
+    for (i = 0; i < trace_event_count(); i++) {
+        TraceEvent *ev = trace_event_id(i);
+        if (strcmp(trace_event_get_name(ev), name) == 0) {
+            return ev;
+        }
+    }
+    return NULL;
+}
+
+static bool glob(const char *pat, const char *ev)
+{
+    while (*pat != '\0' && *ev != '\0') {
+        if (*pat == *ev) {
+            pat++;
+            ev++;
+        }
+        else if (*pat == '*') {
+            if (glob(pat, ev+1)) {
+                return true;
+            } else if (glob(pat+1, ev)) {
+                return true;
+            } else {
+                return false;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    while (*pat == '*') {
+        pat++;
+    }
+
+    if (*pat == '\0' && *ev == '\0') {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+TraceEvent *trace_event_pattern(const char *pat, TraceEvent *ev)
 {
-    int ret;
+    assert(pat != NULL);
 
+    TraceEventID i;
+
+    if (ev == NULL) {
+        i = -1;
+    } else {
+        i = trace_event_get_id(ev);
+    }
+    i++;
+
+    while (i < trace_event_count()) {
+        TraceEvent *res = trace_event_id(i);
+        if (glob(pat, trace_event_get_name(res))) {
+            return res;
+        }
+        i++;
+    }
+
+    return NULL;
+}
+
+void trace_backend_init_events(const char *fname)
+{
     if (fname == NULL) {
         return;
     }
@@ -32,15 +99,28 @@  void trace_backend_init_events(const char *fname)
             if ('#' == line_buf[0]) { /* skip commented lines */
                 continue;
             }
-            if ('-' == line_buf[0]) {
-                ret = trace_event_set_state(line_buf+1, false);
+            const bool enable = ('-' != line_buf[0]);
+            char *line_ptr = enable ? line_buf : line_buf + 1;
+            if (trace_event_is_pattern(line_ptr)) {
+                TraceEvent *ev = NULL;
+                while ((ev = trace_event_pattern(line_ptr, ev)) != NULL) {
+                    if (trace_event_get_state_static(ev)) {
+                        trace_event_set_state_dynamic(ev, enable);
+                    }
+                }
             } else {
-                ret = trace_event_set_state(line_buf, true);
-            }
-            if (!ret) {
-                fprintf(stderr,
-                        "error: trace event '%s' does not exist\n", line_buf);
-                exit(1);
+                TraceEvent *ev = trace_event_name(line_ptr);
+                if (ev == NULL) {
+                    fprintf(stderr,
+                            "error: trace event '%s' does not exist\n", line_ptr);
+                    exit(1);
+                }
+                if (!trace_event_get_state_static(ev)) {
+                    fprintf(stderr,
+                            "error: trace event '%s' is not traceable\n", line_ptr);
+                    exit(1);
+                }
+                trace_event_set_state_dynamic(ev, enable);
             }
         }
     }
diff --git a/trace/control.h b/trace/control.h
index 2acaa42..fbb193d 100644
--- a/trace/control.h
+++ b/trace/control.h
@@ -1,41 +1,195 @@ 
 /*
  * Interface for configuring and controlling the state of tracing events.
  *
- * Copyright (C) 2011 Lluís Vilanova <vilanova@ac.upc.edu>
+ * Copyright (C) 2011-2012 Lluís Vilanova <vilanova@ac.upc.edu>
  *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
  */
 
-#ifndef TRACE_CONTROL_H
-#define TRACE_CONTROL_H
+#ifndef TRACE__CONTROL_H
+#define TRACE__CONTROL_H
 
 #include "qemu-common.h"
+#include "trace/generated-events.h"
 
 
-/** Print the state of all events. */
-void trace_print_events(FILE *stream, fprintf_function stream_printf);
-/** Set the state of an event.
+/**
+ * TraceEventID:
+ *
+ * Unique tracing event identifier.
+ *
+ * These are named as 'TRACE_${EVENT_NAME}'.
  *
- * @return Whether the state changed.
+ * See also: "trace/generated-events.h"
  */
-bool trace_event_set_state(const char *name, bool state);
+enum TraceEventID;
 
+/**
+ * trace_event_id:
+ * @id: Event identifier.
+ *
+ * Get an event by its identifier.
+ *
+ * This routine has a constant cost, as opposed to trace_event_name and
+ * trace_event_pattern.
+ *
+ * Pre-conditions: The identifier is valid.
+ *
+ * Returns: pointer to #TraceEvent.
+ *
+ */
+static TraceEvent *trace_event_id(TraceEventID id);
+
+/**
+ * trace_event_name:
+ * @id: Event name.
+ *
+ * Search an event by its name.
+ *
+ * Returns: pointer to #TraceEvent or NULL if not found.
+ */
+TraceEvent *trace_event_name(const char *name);
+
+/**
+ * trace_event_pattern:
+ * @pat: Event name pattern.
+ * @ev: Event to start searching from (not included).
+ *
+ * Get all events with a given name pattern.
+ *
+ * Returns: pointer to #TraceEvent or NULL if not found.
+ */
+TraceEvent *trace_event_pattern(const char *pat, TraceEvent *ev);
 
-/** Initialize the tracing backend.
+/**
+ * trace_event_is_pattern:
+ *
+ * Whether the given string is an event name pattern.
+ */
+static bool trace_event_is_pattern(const char *str);
+
+/**
+ * trace_event_count:
+ *
+ * Return the number of events.
+ */
+static TraceEventID trace_event_count(void);
+
+
+
+/**
+ * trace_event_get_id:
+ *
+ * Get the identifier of an event.
+ */
+static TraceEventID trace_event_get_id(TraceEvent *ev);
+
+/**
+ * trace_event_get_name:
+ *
+ * Get the name of an event.
+ */
+static const char * trace_event_get_name(TraceEvent *ev);
+
+/**
+ * trace_event_get_state:
+ * @id: Event identifier.
+ *
+ * Get the tracing state of an event (both static and dynamic).
+ *
+ * If the event has the disabled property, the check will have no performance
+ * impact.
  *
- * @events Name of file with events to be enabled at startup; may be NULL.
- *         Corresponds to commandline option "-trace events=...".
- * @file   Name of trace output file; may be NULL.
- *         Corresponds to commandline option "-trace file=...".
- * @return Whether the backend could be successfully initialized.
+ * As a down side, you must always use an immediate #TraceEventID value.
+ */
+#define trace_event_get_state(id)                       \
+    ((id ##_ENABLED) && trace_event_get_state_dynamic(trace_event_id(id)))
+
+/**
+ * trace_event_get_state_static:
+ * @id: Event identifier.
+ *
+ * Get the static tracing state of an event.
+ *
+ * Use the define 'TRACE_${EVENT_NAME}_ENABLED' for compile-time checks (it will
+ * be set to 1 or 0 according to the presence of the disabled property).
+ */
+static bool trace_event_get_state_static(TraceEvent *ev);
+
+/**
+ * trace_event_get_state_dynamic:
+ *
+ * Get the dynamic tracing state of an event.
+ */
+static bool trace_event_get_state_dynamic(TraceEvent *ev);
+
+/**
+ * trace_event_set_state:
+ *
+ * Set the tracing state of an event (only if possible).
+ */
+#define trace_event_set_state(id, state)                \
+    do {                                                \
+        if ((id ##_ENABLED)) {                          \
+            TraceEvent *_e = trace_event_id(id);        \
+            trace_event_set_state_dynamic(_e, state);   \
+        }                                               \
+    } while (0)
+
+/**
+ * trace_event_set_state_dynamic:
+ *
+ * Set the dynamic tracing state of an event.
+ *
+ * Pre-condition: trace_event_get_state_static(ev) == true
+ */
+static void trace_event_set_state_dynamic(TraceEvent *ev, bool state);
+
+/**
+ * trace_event_set_state_dynamic_backend:
+ *
+ * Warning: This function must be implemented by each tracing backend.
+ */
+void trace_event_set_state_dynamic_backend(TraceEvent *ev, bool state);
+
+
+
+/**
+ * trace_print_events:
+ *
+ * Print the state of all events.
+ *
+ * Warning: This function must be implemented by each tracing backend.
+ *
+ * TODO: Should this be moved to generic code?
+ */
+void trace_print_events(FILE *stream, fprintf_function stream_printf);
+
+/**
+ * trace_backend_init:
+ * @events: Name of file with events to be enabled at startup; may be NULL.
+ *          Corresponds to commandline option "-trace events=...".
+ * @file:   Name of trace output file; may be NULL.
+ *          Corresponds to commandline option "-trace file=...".
+ *
+ * Initialize the tracing backend.
+ *
+ * Warning: This function must be implemented by each tracing backend.
+ *
+ * Returns: Whether the backend could be successfully initialized.
  */
 bool trace_backend_init(const char *events, const char *file);
 
-/** Generic function to initialize the state of events.
+/**
+ * trace_backend_init_events:
+ * @fname: Name of file with events to enable; may be NULL.
  *
- * @fname Name of file with events to enable; may be NULL.
+ * Generic function to initialize the state of events.
  */
 void trace_backend_init_events(const char *fname);
 
-#endif  /* TRACE_CONTROL_H */
+
+#include "trace/control-internal.h"
+
+#endif  /* TRACE__CONTROL_H */