Skip to contentSkip to conversation

Monitor support QEMU trace framework

PATCH In review Prerna Saxena 4 participants 11 messages Jun 16, 2010 – Jun 18, 2010

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover Monitor support QEMU trace framework

This is v3 of a set of patches to enable trace visualization & control via the QEMU monitor. It is based on trace infrastructure posted by Stefan :
( http://lists.gnu.org/archive/html/qemu-devel/2010-05/msg02407.html )

This patchset adds monitor commands :
- info trace : to view current contents of the trace buffer
- info tracepoints : to view all available tracepoints and their state.
- tracepoint NAME on|off : to enable/disable the logging of data from tracepoint 'NAME' to on/off.

Changelog :
- Clean-ups from v2, particularly relating to export of tdb_hash()

Signature · 5 lines
Prerna Saxena

Linux Technology Centre,
IBM Systems and Technology Lab,
Bangalore, India
#1 lore raw

PATCH 1/3 Export hash function

Rename tdb_hash() to qemu_hash().
Move definition from qdict.c to a new file qemu-misc.c for use by tracing infrastructure.

---

 Makefile.objs |    2 +-
 qdict.c       |   24 ++++--------------------
 qemu-common.h |    3 +++
 qemu-misc.c   |   24 ++++++++++++++++++++++++
 4 files changed, 32 insertions(+), 21 deletions(-)
 create mode 100644 qemu-misc.c
Makefile.objs +1 -1
index 7cb40ac..53e3a65 100644
@@ -1,6 +1,6 @@
 #######################################################################
 # QObject
-qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
+qobject-obj-y = qint.o qstring.o qdict.o qemu-misc.o qlist.o qfloat.o qbool.o
 qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o
 qobject-obj-y += qerror.o
 
qdict.c +4 -20
index 175bc17..d4588ef 100644
@@ -53,22 +53,6 @@ QDict *qobject_to_qdict(const QObject *obj)
 }
 
 /**
- * tdb_hash(): based on the hash agorithm from gdbm, via tdb
- * (from module-init-tools)
- */
-static unsigned int tdb_hash(const char *name)
-{
-    unsigned value;	/* Used to compute the hash value.  */
-    unsigned   i;	/* Used to cycle through random values. */
-
-    /* Set the initial value from the key size. */
-    for (value = 0x238F13AF * strlen(name), i=0; name[i]; i++)
-        value = (value + (((const unsigned char *)name)[i] << (i*5 % 24)));
-
-    return (1103515243 * value + 12345);
-}
-
-/**
  * alloc_entry(): allocate a new QDictEntry
  */
 static QDictEntry *alloc_entry(const char *key, QObject *value)
@@ -113,7 +97,7 @@ void qdict_put_obj(QDict *qdict, const char *key, QObject *value)
     unsigned int hash;
     QDictEntry *entry;
 
-    hash = tdb_hash(key) % QDICT_HASH_SIZE;
+    hash = qemu_hash(key) % QDICT_HASH_SIZE;
     entry = qdict_find(qdict, key, hash);
     if (entry) {
         /* replace key's value */
@@ -137,7 +121,7 @@ QObject *qdict_get(const QDict *qdict, const char *key)
 {
     QDictEntry *entry;
 
-    entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_HASH_SIZE);
+    entry = qdict_find(qdict, key, qemu_hash(key) % QDICT_HASH_SIZE);
     return (entry == NULL ? NULL : entry->value);
 }
 
@@ -148,7 +132,7 @@ QObject *qdict_get(const QDict *qdict, const char *key)
  */
 int qdict_haskey(const QDict *qdict, const char *key)
 {
-    unsigned int hash = tdb_hash(key) % QDICT_HASH_SIZE;
+    unsigned int hash = qemu_hash(key) % QDICT_HASH_SIZE;
     return (qdict_find(qdict, key, hash) == NULL ? 0 : 1);
 }
 
@@ -347,7 +331,7 @@ void qdict_del(QDict *qdict, const char *key)
 {
     QDictEntry *entry;
 
-    entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_HASH_SIZE);
+    entry = qdict_find(qdict, key, qemu_hash(key) % QDICT_HASH_SIZE);
     if (entry) {
         QLIST_REMOVE(entry, next);
         qentry_destroy(entry);
qemu-common.h +3 -0
index a4888e5..d225e45 100644
@@ -17,6 +17,9 @@ typedef struct QEMUTimer QEMUTimer;
 typedef struct QEMUFile QEMUFile;
 typedef struct QEMUBH QEMUBH;
 
+/* Hash function definition */
+unsigned int qemu_hash(const char *name);
+
 /* Hack around the mess dyngen-exec.h causes: We need QEMU_NORETURN in files that
    cannot include the following headers without conflicts. This condition has
    to be removed once dyngen is gone. */
qemu-misc.c new file +24 -0
new file mode 100644
index 0000000..a69196a
@@ -0,0 +1,24 @@
+/*
+ * Definition of tdb_hash() moved here, from qdict.c. Renamed to qemu_hash()
+ * 
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+#include "qemu-common.h"
+
+/**
+ * tdb_hash(): based on the hash agorithm from gdbm, via tdb
+ * (from module-init-tools). Renamed to qemu_hash(). 
+ */
+unsigned int qemu_hash(const char *name)
+{
+    unsigned value;     /* Used to compute the hash value.  */
+    unsigned   i;       /* Used to cycle through random values. */
+
+    /* Set the initial value from the key size. */
+    for (value = 0x238F13AF * strlen(name), i=0; name[i]; i++)
+        value = (value + (((const unsigned char *)name)[i] << (i*5 % 24)));
+
+    return (1103515243 * value + 12345);
+}
Signature · 5 lines
Prerna Saxena

Linux Technology Centre,
IBM Systems and Technology Lab,
Bangalore, India
#2 lore raw

PATCH 2/3 Monitor command 'info trace'

In reply to Prerna Saxena

Monitor command 'info trace' to display contents of trace buffer

---

 configure       |    3 +++
 monitor.c       |   12 ++++++++++++
 qemu-monitor.hx |    4 ++++
 simpletrace.c   |   13 +++++++++++++
 tracetool       |    2 ++
 5 files changed, 34 insertions(+), 0 deletions(-)
configure +3 -0
index 675d0fc..56af8dd 100755
@@ -2302,6 +2302,9 @@ bsd)
 esac
 
 echo "TRACE_BACKEND=$trace_backend" >> $config_host_mak
+if test "$trace_backend" = "simple"; then
+  echo "CONFIG_SIMPLE_TRACE=y" >> $config_host_mak
+fi
 if test "$trace_backend" = "ust"; then
   LIBS="-lust $LIBS"
 fi
monitor.c +12 -0
index ad50f12..8b60830 100644
@@ -55,6 +55,9 @@
 #include "json-streamer.h"
 #include "json-parser.h"
 #include "osdep.h"
+#ifdef CONFIG_SIMPLE_TRACE
+#include "trace.h"
+#endif
 
 //#define DEBUG
 //#define DEBUG_COMPLETION
@@ -2780,6 +2783,15 @@ static const mon_cmd_t info_cmds[] = {
         .help       = "show roms",
         .mhandler.info = do_info_roms,
     },
+#if defined(CONFIG_SIMPLE_TRACE)
+    {
+        .name       = "trace",
+        .args_type  = "",
+        .params     = "",
+        .help       = "show current contents of trace buffer",
+        .mhandler.info = do_info_trace,
+    },
+#endif
     {
         .name       = NULL,
     },
qemu-monitor.hx +4 -0
index b6e3467..766c30f 100644
@@ -114,6 +114,10 @@ show migration status
 show balloon information
 @item info qtree
 show device tree
+#ifdef CONFIG_SIMPLE_TRACE
+@item info trace
+show contents of trace buffer
+#endif
 @end table
 ETEXI
 
simpletrace.c +13 -0
index 2fec4d3..239ae3f 100644
@@ -62,3 +62,16 @@ void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long
 void trace5(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4, unsigned long x5) {
     trace(event, x1, x2, x3, x4, x5);
 }
+
+void do_info_trace(Monitor *mon)
+{
+    unsigned int i, max_idx;
+
+    max_idx = trace_idx ? trace_idx : TRACE_BUF_LEN;
+
+    for (i=0; i<max_idx ;i++) {
+        monitor_printf(mon, "Event %ld : %ld %ld %ld %ld %ld\n",
+                          trace_buf[i].event, trace_buf[i].x1, trace_buf[i].x2,
+                            trace_buf[i].x3, trace_buf[i].x4, trace_buf[i].x5);
+    }
+}
tracetool +2 -0
index 9ea9c08..2c73bab 100755
@@ -130,6 +130,7 @@ void trace2(TraceEvent event, unsigned long x1, unsigned long x2);
 void trace3(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3);
 void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4);
 void trace5(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4, unsigned long x5);
+void do_info_trace(Monitor *mon);
 EOF
 
     simple_event_num=0
@@ -289,6 +290,7 @@ tracetoh()
 #define TRACE_H
 
 #include "qemu-common.h"
+#include "monitor.h"
 EOF
     convert h
     echo "#endif /* TRACE_H */"
Signature · 5 lines
Prerna Saxena

Linux Technology Centre,
IBM Systems and Technology Lab,
Bangalore, India
#3 lore raw

Re: [PATCH 2/3] Monitor command 'info trace'

Quoted 5 lines · Prerna Saxena
simpletrace.c +0 -0
index 2fec4d3..239ae3f 100644

simpletrace.c void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long · line 70

Quoted 9 lines
simpletrace.c +6 -0
@@ -62,3 +62,16 @@ void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long
 void trace5(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4, unsigned long x5) {
     trace(event, x1, x2, x3, x4, x5);
 }
+
+void do_info_trace(Monitor *mon)
+{
+    unsigned int i, max_idx;
+
+    max_idx = trace_idx ? trace_idx : TRACE_BUF_LEN;

trace_idx is always in the range [0, TRACE_BUF_LEN). There is no need to perform this test.

simpletrace.c void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long · line 72

Quoted 2 lines
simpletrace.c +2 -0
+
+    for (i=0; i<max_idx ;i++) {

Whitespace "i=0; i<max_idx ;i++". "i = 0; i < max_idx; i++" is pretty common across QEMU.

simpletrace.c void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long · line 75

Quoted 3 lines
simpletrace.c +3 -0
+        monitor_printf(mon, "Event %ld : %ld %ld %ld %ld %ld\n",
+                          trace_buf[i].event, trace_buf[i].x1, trace_buf[i].x2,
+                            trace_buf[i].x3, trace_buf[i].x4, trace_buf[i].x5);

Getting only numeric output is the limitation of a binary trace. It would probably be possible to pretty-print without much additional code by using the format strings from the trace-events file.

I think the numeric dump is good for now though. Hex is more compact than decimal and would make pointers easier to spot. Want to change this?

Quoted 15 lines
simpletrace.c +2 -0
+    }
+}
tracetool +1 -0
index 9ea9c08..2c73bab 100755
@@ -130,6 +130,7 @@ void trace2(TraceEvent event, unsigned long x1, unsigned long x2);
 void trace3(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3);
 void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4);
 void trace5(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4, unsigned long x5);
+void do_info_trace(Monitor *mon);
 EOF
 
     simple_event_num=0

tracetool tracetoh() · line 293

Quoted 4 lines
tracetool +1 -0
@@ -289,6 +290,7 @@ tracetoh()
 #define TRACE_H
 
 #include "qemu-common.h"
+#include "monitor.h"

qemu-common.h forward-declares Monitor, I don't think you need monitor.h.

Stefan

#4 lore raw

Re: [PATCH 2/3] Monitor command 'info trace'

Hi Stefan, Jan,
Thanks for taking a look.

Quoted 10 lines · Stefan Hajnoczi
Quoted 9 lines · Prerna Saxena
simpletrace.c +1 -0
index 2fec4d3..239ae3f 100644
@@ -62,3 +62,16 @@ void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long
  void trace5(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4, unsigned long x5) {
      trace(event, x1, x2, x3, x4, x5);
  }
+
Quoted 8 lines

simpletrace.c void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long · line 70

Quoted 5 lines
simpletrace.c +5 -0
+void do_info_trace(Monitor *mon)
+{
+    unsigned int i, max_idx;
+
+    max_idx = trace_idx ? trace_idx : TRACE_BUF_LEN;

trace_idx is always in the range [0, TRACE_BUF_LEN). There is no need to perform this test.

I only display the logged contents in the trace buffer (till trace_idx) , and not the entire trace buffer. Only when the index is full that the entire buffer is displayed.

Quoted 7 lines

simpletrace.c void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long · line 72

Quoted 3 lines
simpletrace.c +2 -0
+
+    for (i=0; i<max_idx ;i++) {

Whitespace "i=0; i<max_idx ;i++". "i = 0; i< max_idx; i++" is pretty common across QEMU.

I'll fix this.

Quoted 4 lines

simpletrace.c void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long · line 75

Quoted 3 lines
simpletrace.c +3 -0
+        monitor_printf(mon, "Event %ld : %ld %ld %ld %ld %ld\n",
+                          trace_buf[i].event, trace_buf[i].x1, trace_buf[i].x2,
+                            trace_buf[i].x3, trace_buf[i].x4, trace_buf[i].x5);
Quoted 8 lines

Getting only numeric output is the limitation of a binary trace. It would probably be possible to pretty-print without much additional code by using the format strings from the trace-events file.

I think the numeric dump is good for now though. Hex is more compact than decimal and would make pointers easier to spot. Want to change this?

I agree, but we can let this be a todo till after the first prototype goes upstream ?

Quoted 17 lines
Quoted 17 lines
simpletrace.c +2 -0
+    }
+}
tracetool +1 -0
index 9ea9c08..2c73bab 100755
@@ -130,6 +130,7 @@ void trace2(TraceEvent event, unsigned long x1, unsigned long x2);
  void trace3(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3);
  void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4);
  void trace5(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4, unsigned long x5);
+void do_info_trace(Monitor *mon);
  EOF
 
      simple_event_num=0
@@ -289,6 +290,7 @@ tracetoh()
  #define TRACE_H
 
Quoted 6 lines

tracetool tracetoh() · line 293

Quoted 2 lines
tracetool +1 -0
  #include "qemu-common.h"
+#include "monitor.h"

qemu-common.h forward-declares Monitor, I don't think you need monitor.h.

Right.

Quoted 1 line

Stefan

I'll post patches by Monday that addresses your suggestions, and try to get it integrated with QMP.

Signature · 5 lines
Prerna Saxena

Linux Technology Centre,
IBM Systems and Technology Lab,
Bangalore, India
#5 lore raw

Re: [PATCH 2/3] Monitor command 'info trace'

Quoted 27 lines · Prerna Saxena

Hi Stefan, Jan,
Thanks for taking a look.

Quoted 14 lines · Stefan Hajnoczi
Quoted 12 lines · Prerna Saxena
simpletrace.c +0 -0
index 2fec4d3..239ae3f 100644
@@ -62,3 +62,16 @@ void trace4(TraceEvent event, unsigned long x1,

unsigned long x2, unsigned long
 void trace5(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4, unsigned long x5) { trace(event, x1, x2, x3, x4, x5);
 }
+

Quoted 8 lines

simpletrace.c void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long · line 70

Quoted 5 lines
simpletrace.c +5 -0
+void do_info_trace(Monitor *mon)
+{
+    unsigned int i, max_idx;
+
+    max_idx = trace_idx ? trace_idx : TRACE_BUF_LEN;

trace_idx is always in the range [0, TRACE_BUF_LEN).  There is no need to perform this test.

Quoted 3 lines

I only display the logged contents in the trace buffer (till trace_idx) , and not the entire trace buffer. Only when the index is full that the entire buffer is displayed.

Thanks for explaining, I understand what you are doing now. Due to this special case, the code will dump out the empty trace buffer if used before anything has been traced (trace_idx=0).

Quoted 10 lines
Quoted 10 lines
Quoted 5 lines

+        monitor_printf(mon, "Event %ld : %ld %ld %ld %ld %ld\n",
+                          trace_buf[i].event, trace_buf[i].x1, trace_buf[i].x2,
+                            trace_buf[i].x3, trace_buf[i].x4, trace_buf[i].x5);

Getting only numeric output is the limitation of a binary trace.  It would probably be possible to pretty-print without much additional code by using the format strings from the trace-events file.

Quoted 7 lines
Quoted 4 lines

I think the numeric dump is good for now though.  Hex is more compact than decimal and would make pointers easier to spot.  Want to change this?

I agree, but we can let this be a todo till after the first prototype goes upstream ?

I still vote for hex instead of decimal :). Since you're already spinning a new patch it would be nice to put that change in, but no worries.

Quoted 2 lines

I'll post patches by Monday that addresses your suggestions, and try to get it integrated with QMP.

Excellent, thanks. I'd like to put your patches onto my tracing branch soon and test out the overall workflow of tracing QEMU.

Stefan

#6 lore raw

PATCH 3/3 Toggle tracepoint state

In reply to Prerna Saxena

This patch adds support for dynamically enabling/disabling of tracepoints. This is done by internally maintaining each tracepoint's state, and permitting logging of data from a tracepoint only if it is in an 'active' state.

Monitor commands added :
1) info tracepoints : to view all available tracepoints and
their state.
2) tracepoint NAME on|off : to enable/disable data logging from a
given tracepoint.
Eg, tracepoint paio_submit off
disables logging of data when paio_submit is hit.

---

 monitor.c       |   16 ++++++++++++++
 qemu-monitor.hx |   18 ++++++++++++++++
 simpletrace.c   |   63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tracetool       |   30 +++++++++++++++++++++++---
 vl.c            |    6 +++++
 5 files changed, 129 insertions(+), 4 deletions(-)
monitor.c +16 -0
index 8b60830..238bdf0 100644
@@ -548,6 +548,15 @@ static void do_commit(Monitor *mon, const QDict *qdict)
     }
 }
 
+#ifdef CONFIG_SIMPLE_TRACE
+static void do_change_tracepoint_state(Monitor *mon, const QDict *qdict)
+{
+    const char *tp_name = qdict_get_str(qdict, "name");
+    bool new_state = qdict_get_bool(qdict, "option");
+    change_tracepoint_state(tp_name, new_state);
+}
+#endif
+
 static void user_monitor_complete(void *opaque, QObject *ret_data)
 {
     MonitorCompletionData *data = (MonitorCompletionData *)opaque; 
@@ -2791,6 +2800,13 @@ static const mon_cmd_t info_cmds[] = {
         .help       = "show current contents of trace buffer",
         .mhandler.info = do_info_trace,
     },
+    {
+        .name       = "tracepoints",
+        .args_type  = "",
+        .params     = "",
+        .help       = "show available tracepoints & their state",
+        .mhandler.info = do_info_all_tracepoints,
+    },
 #endif
     {
         .name       = NULL,
qemu-monitor.hx +18 -0
index 766c30f..8540b8f 100644
@@ -117,6 +117,8 @@ show device tree
 #ifdef CONFIG_SIMPLE_TRACE
 @item info trace
 show contents of trace buffer
+@item info tracepoints
+show available tracepoints and their state
 #endif
 @end table
 ETEXI
@@ -225,6 +227,22 @@ STEXI
 @item logfile @var{filename}
 @findex logfile
 Output logs to @var{filename}.
+#ifdef CONFIG_SIMPLE_TRACE
+ETEXI
+
+    {
+        .name       = "tracepoint",
+        .args_type  = "name:s,option:b",
+        .params     = "name on|off",
+        .help       = "changes status of a specific tracepoint",
+        .mhandler.cmd = do_change_tracepoint_state,
+    },
+
+STEXI
+@item tracepoint
+@findex tracepoint
+changes status of a tracepoint
+#endif
 ETEXI
 
     {
simpletrace.c +63 -0

83 lines not on this page · raw message

tracetool +26 -4
index 2c73bab..00af205 100755
@@ -123,14 +123,20 @@ linetoc_end_nop()
 linetoh_begin_simple()
 {
     cat <<EOF
+#include <stdbool.h>
+
 typedef unsigned int TraceEvent;
 
+void init_tracepoint_table(void);
+void init_tracepoint(const char *tname, TraceEvent tevent);
 void trace1(TraceEvent event, unsigned long x1);
 void trace2(TraceEvent event, unsigned long x1, unsigned long x2);
 void trace3(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3);
 void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4);
 void trace5(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4, unsigned long x5);
 void do_info_trace(Monitor *mon);
+void do_info_all_tracepoints(Monitor *mon);
+void change_tracepoint_state(const char *tname, bool tstate);
 EOF
 
     simple_event_num=0
@@ -163,22 +169,38 @@ EOF
 
 linetoh_end_simple()
 {
-    return
+    cat <<EOF
+#define NR_TRACEPOINTS $simple_event_num
+EOF
 }
 
 linetoc_begin_simple()
 {
-    return
+    cat <<EOF
+#include "trace.h"
+
+void init_tracepoint_table(void) {
+EOF
+    simple_event_num=0
+
 }
 
 linetoc_simple()
 {
-    return
+    local name
+    name=$(get_name "$1")
+    cat <<EOF
+init_tracepoint("$name", $simple_event_num);
+EOF
+    simple_event_num=$((simple_event_num + 1))
 }
 
 linetoc_end_simple()
 {
-    return
+    cat <<EOF
+return;
+}
+EOF
 }
 
 linetoh_begin_ust()
vl.c +6 -0
index 328395e..dd07904 100644
@@ -163,6 +163,9 @@ int main(int argc, char **argv)
 #include "cpus.h"
 #include "arch_init.h"
 
+#ifdef CONFIG_SIMPLE_TRACE
+#include "trace.h"
+#endif
 //#define DEBUG_NET
 //#define DEBUG_SLIRP
 
@@ -3604,6 +3607,9 @@ int main(int argc, char **argv, char **envp)
     if (net_init_clients() < 0) {
         exit(1);
     }
+#ifdef CONFIG_SIMPLE_TRACE
+    init_tracepoint_table();
+#endif
 
     /* init the bluetooth world */
     if (foreach_device_config(DEV_BT, bt_parse))
Signature · 5 lines
Prerna Saxena

Linux Technology Centre,
IBM Systems and Technology Lab,
Bangalore, India
#7 lore raw

Re: [PATCH 3/3] Toggle tracepoint state

Quoted 100 lines · Prerna Saxena

This patch adds support for dynamically enabling/disabling of tracepoints. This is done by internally maintaining each tracepoint's state, and permitting logging of data from a tracepoint only if it is in an 'active' state.

Monitor commands added :
1) info tracepoints : to view all available tracepoints and
their state.
2) tracepoint NAME on|off : to enable/disable data logging from a
given tracepoint.
Eg, tracepoint paio_submit off
disables logging of data when paio_submit is hit.

---

 monitor.c       |   16 ++++++++++++++
 qemu-monitor.hx |   18 ++++++++++++++++
 simpletrace.c   |   63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tracetool       |   30 +++++++++++++++++++++++---
 vl.c            |    6 +++++
 5 files changed, 129 insertions(+), 4 deletions(-)
monitor.c +16 -0
index 8b60830..238bdf0 100644
@@ -548,6 +548,15 @@ static void do_commit(Monitor *mon, const QDict *qdict)
     }
 }
 
+#ifdef CONFIG_SIMPLE_TRACE
+static void do_change_tracepoint_state(Monitor *mon, const QDict *qdict)
+{
+    const char *tp_name = qdict_get_str(qdict, "name");
+    bool new_state = qdict_get_bool(qdict, "option");
+    change_tracepoint_state(tp_name, new_state);
+}
+#endif
+
 static void user_monitor_complete(void *opaque, QObject *ret_data)
 {
     MonitorCompletionData *data = (MonitorCompletionData *)opaque; 
@@ -2791,6 +2800,13 @@ static const mon_cmd_t info_cmds[] = {
         .help       = "show current contents of trace buffer",
         .mhandler.info = do_info_trace,
     },
+    {
+        .name       = "tracepoints",
+        .args_type  = "",
+        .params     = "",
+        .help       = "show available tracepoints & their state",
+        .mhandler.info = do_info_all_tracepoints,
+    },
 #endif
     {
         .name       = NULL,
qemu-monitor.hx +18 -0
index 766c30f..8540b8f 100644
@@ -117,6 +117,8 @@ show device tree
 #ifdef CONFIG_SIMPLE_TRACE
 @item info trace
 show contents of trace buffer
+@item info tracepoints
+show available tracepoints and their state
 #endif
 @end table
 ETEXI
@@ -225,6 +227,22 @@ STEXI
 @item logfile @var{filename}
 @findex logfile
 Output logs to @var{filename}.
+#ifdef CONFIG_SIMPLE_TRACE
+ETEXI
+
+    {
+        .name       = "tracepoint",
+        .args_type  = "name:s,option:b",
+        .params     = "name on|off",
+        .help       = "changes status of a specific tracepoint",
+        .mhandler.cmd = do_change_tracepoint_state,
+    },
+
+STEXI
+@item tracepoint
+@findex tracepoint
+changes status of a tracepoint
+#endif
 ETEXI
 
     {
simpletrace.c +0 -0
index 239ae3f..4221a8f 100644

simpletrace.c line 9

Quoted 7 lines
simpletrace.c +4 -0
@@ -3,6 +3,12 @@
 #include "trace.h"
 
 typedef struct {
+    char *tp_name;
+    bool state;
+    unsigned int hash;
+} Tracepoint;

The tracing infrastructure avoids using the name 'tracepoint'. It calls them trace events. I didn't deliberately choose that name, but was unaware at the time that Linux tracing calls them tracepoints. Given that 'trace event' is currently used, it would be nice to remain consistent/reduce confusion.

How about:

typedef struct {
    const char *name;
    bool enabled;
    unsigned int hash;
} TraceEventState;

Or a nicer overall change might be to rename enum TraceEvent to TraceEventID and Tracepoint to TraceEvent.

Quoted 6 lines
simpletrace.c +2 -0
+
+typedef struct {
     unsigned long event;
     unsigned long x1;
     unsigned long x2;

simpletrace.c enum { · line 33

Quoted 10 lines
simpletrace.c +7 -0
@@ -18,11 +24,29 @@ enum {
 static TraceRecord trace_buf[TRACE_BUF_LEN];
 static unsigned int trace_idx;
 static FILE *trace_fp;
+static Tracepoint trace_list[NR_TRACEPOINTS];
+
+void init_tracepoint(const char *tname, TraceEvent tevent)
+{
+    if (!tname || tevent > NR_TRACEPOINTS) {
+        return;
+    }

I'd drop this check because only trace.c should use init_tracepoint() and you have ensured it uses correct arguments. Just a coding style suggestion; having redundant checks makes the code more verbose, may lead the reader to assume that this function really is called with junk arguments, and silently returning will not help make the issue visible.

simpletrace.c enum { · line 36

Quoted 2 lines
simpletrace.c +2 -0
+    trace_list[tevent].tp_name = (char*)qemu_malloc(strlen(tname)+1);
+    strncpy(trace_list[tevent].tp_name, tname, strlen(tname));

Or use qemmu_strdup() but we don't really need to allocate memory at all here. Just hold the const char* to a string literal since the trace event is a static object that is built into the binary.

Quoted 19 lines
simpletrace.c +8 -0
+    trace_list[tevent].hash = qemu_hash(tname);
+    trace_list[tevent].state = 1; /* Enable all by default */
+}
 
 static void trace(TraceEvent event, unsigned long x1,
                   unsigned long x2, unsigned long x3,
                   unsigned long x4, unsigned long x5) {
     TraceRecord *rec = &trace_buf[trace_idx];
+
+    if (!trace_list[event].state) {
+        return;
+    }
+
     rec->event = event;
     rec->x1 = x1;
     rec->x2 = x2;
@@ -75,3 +99,42 @@ void do_info_trace(Monitor *mon)
                             trace_buf[i].x3, trace_buf[i].x4, trace_buf[i].x5);
     }

simpletrace.c void do_info_trace(Monitor *mon) · line 110

Quoted 10 lines
simpletrace.c +9 -0
 }
+
+void do_info_all_tracepoints(Monitor *mon)
+{
+    unsigned int i;
+
+    for (i=0; i<NR_TRACEPOINTS; i++) {
+        monitor_printf(mon, "%s [Event ID %u] : state %u\n",
+                                trace_list[i].tp_name, i, trace_list[i].state);
+        }

Whitespace and indentation.

Quoted 9 lines
simpletrace.c +9 -0
+}
+
+static Tracepoint* find_tracepoint_by_name(const char *tname)
+{
+    unsigned int i, name_hash;
+
+    if (!tname) {
+        return NULL;
+    }

simpletrace.c void do_info_trace(Monitor *mon) · line 129

Quoted 10 lines
simpletrace.c +10 -0
+
+    name_hash = qemu_hash(tname);
+
+    for (i=0; i<NR_TRACEPOINTS; i++) {
+        if (trace_list[i].hash == name_hash &&
+                       !strncmp(trace_list[i].tp_name, tname, strlen(tname))) {
+            return &trace_list[i];
+        }
+    }
+    return NULL; /* indicates end of list reached without a match */

I don't see the need for hashing. We don't actually have a hash table and are doing a linear search. There will be a smallish constant number of trace events and only change_tracepoint_by_name() needs to do a lookup. There's no need to optimize that.

Is strncmp() used so looking up "foo" is like searching for foo*? The strlen(tname) should be outside the loop. I think that could be useful but we should document it or just use strcmp().

Quoted 44 lines
simpletrace.c +11 -0
+}
+
+void change_tracepoint_state(const char *tname, bool tstate)
+{
+    Tracepoint *tp;
+
+    tp = find_tracepoint_by_name(tname);
+    if (tp) {
+        tp->state = tstate;
+    }
+}
tracetool +9 -1
index 2c73bab..00af205 100755
@@ -123,14 +123,20 @@ linetoc_end_nop()
 linetoh_begin_simple()
 {
     cat <<EOF
+#include <stdbool.h>
+
 typedef unsigned int TraceEvent;
 
+void init_tracepoint_table(void);
+void init_tracepoint(const char *tname, TraceEvent tevent);
 void trace1(TraceEvent event, unsigned long x1);
 void trace2(TraceEvent event, unsigned long x1, unsigned long x2);
 void trace3(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3);
 void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4);
 void trace5(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4, unsigned long x5);
 void do_info_trace(Monitor *mon);
+void do_info_all_tracepoints(Monitor *mon);
+void change_tracepoint_state(const char *tname, bool tstate);
 EOF
 
     simple_event_num=0
@@ -163,22 +169,38 @@ EOF
 
 linetoh_end_simple()
 {
-    return
+    cat <<EOF
+#define NR_TRACEPOINTS $simple_event_num
+EOF

tracetool EOF · line 183

Quoted 10 lines
tracetool +5 -1
 }
 
 linetoc_begin_simple()
 {
-    return
+    cat <<EOF
+#include "trace.h"
+
+void init_tracepoint_table(void) {
+EOF

Have you looked at module.h? Perhaps that provides a good solution for initializing trace events. It would allow the vl.c changes to be done without an #ifdef.

Quoted 10 lines
tracetool +5 -1
+    simple_event_num=0
+
 }
 
 linetoc_simple()
 {
-    return
+    local name
+    name=$(get_name "$1")
+    cat <<EOF

tracetool EOF · line 201

Quoted 10 lines
tracetool +5 -1
+init_tracepoint("$name", $simple_event_num);
+EOF
+    simple_event_num=$((simple_event_num + 1))
 }
 
 linetoc_end_simple()
 {
-    return
+    cat <<EOF
+return;

Please don't use return at the end of a void function. Coding style.

Quoted 38 lines
tracetool +2 -0
+}
+EOF
 }
 
 linetoh_begin_ust()
vl.c +6 -0
index 328395e..dd07904 100644
@@ -163,6 +163,9 @@ int main(int argc, char **argv)
 #include "cpus.h"
 #include "arch_init.h"
 
+#ifdef CONFIG_SIMPLE_TRACE
+#include "trace.h"
+#endif
 //#define DEBUG_NET
 //#define DEBUG_SLIRP
 
@@ -3604,6 +3607,9 @@ int main(int argc, char **argv, char **envp)
     if (net_init_clients() < 0) {
         exit(1);
     }
+#ifdef CONFIG_SIMPLE_TRACE
+    init_tracepoint_table();
+#endif
 
     /* init the bluetooth world */
     if (foreach_device_config(DEV_BT, bt_parse))
Signature · 6 lines
-- 
Prerna Saxena

Linux Technology Centre,
IBM Systems and Technology Lab,
Bangalore, India
#8 lore raw

Re: [Qemu-devel] Re: [PATCH 3/3] Toggle tracepoint state

Quoted 20 lines · Stefan Hajnoczi

simpletrace.c line 9

Quoted 11 lines · Prerna Saxena

This patch adds support for dynamically enabling/disabling of tracepoints. This is done by internally maintaining each tracepoint's state, and permitting logging of data from a tracepoint only if it is in an 'active' state.
....
....
typedef struct {

simpletrace.c +4 -0
+    char *tp_name;
+    bool state;
+    unsigned int hash;
+} Tracepoint;

The tracing infrastructure avoids using the name 'tracepoint'. It calls them trace events. I didn't deliberately choose that name, but was unaware at the time that Linux tracing calls them tracepoints. Given that 'trace event' is currently used, it would be nice to remain consistent/reduce confusion.

How about:

Quoted 9 lines
typedef struct {
     const char *name;
     bool enabled;
     unsigned int hash;
} TraceEventState;

Or a nicer overall change might be to rename enum TraceEvent to TraceEventID and Tracepoint to TraceEvent.

I'll change that !

Quoted 14 lines
Quoted 14 lines
simpletrace.c +7 -0
+
+typedef struct {
      unsigned long event;
      unsigned long x1;
      unsigned long x2;
@@ -18,11 +24,29 @@ enum {
  static TraceRecord trace_buf[TRACE_BUF_LEN];
  static unsigned int trace_idx;
  static FILE *trace_fp;
+static Tracepoint trace_list[NR_TRACEPOINTS];
+
+void init_tracepoint(const char *tname, TraceEvent tevent)
+{
+    if (!tname || tevent>  NR_TRACEPOINTS) {
Quoted 8 lines

Review enum { · line 33

Quoted 2 lines
(quoted diff) +2 -0
+        return;
+    }

I'd drop this check because only trace.c should use init_tracepoint() and you have ensured it uses correct arguments. Just a coding style suggestion; having redundant checks makes the code more verbose, may lead the reader to assume that this function really is called with junk arguments, and silently returning will not help make the issue visible.

Ok.

Quoted 8 lines

simpletrace.c enum { · line 36

Quoted 3 lines
simpletrace.c +2 -0
+    trace_list[tevent].tp_name = (char*)qemu_malloc(strlen(tname)+1);
+    strncpy(trace_list[tevent].tp_name, tname, strlen(tname));

Or use qemmu_strdup() but we don't really need to allocate memory at all here. Just hold the const char* to a string literal since the trace event is a static object that is built into the binary.

Ok

Quoted 6 lines
Quoted 6 lines
simpletrace.c +5 -0
+    trace_list[tevent].hash = qemu_hash(tname);
+    trace_list[tevent].state = 1; /* Enable all by default */
+}
...
+void do_info_all_tracepoints(Monitor *mon)
+{
Quoted 9 lines

simpletrace.c void do_info_trace(Monitor *mon) · line 110

Quoted 6 lines
simpletrace.c +6 -0
+    unsigned int i;
+
+    for (i=0; i<NR_TRACEPOINTS; i++) {
+        monitor_printf(mon, "%s [Event ID %u] : state %u\n",
+                                trace_list[i].tp_name, i, trace_list[i].state);
+        }

Whitespace and indentation.

Will fix.

Quoted 16 lines
Quoted 16 lines
simpletrace.c +16 -0
+}
+
+static Tracepoint* find_tracepoint_by_name(const char *tname)
+{
+    unsigned int i, name_hash;
+
+    if (!tname) {
+        return NULL;
+    }
+
+    name_hash = qemu_hash(tname);
+
+    for (i=0; i<NR_TRACEPOINTS; i++) {
+        if (trace_list[i].hash == name_hash&&
+                       !strncmp(trace_list[i].tp_name, tname, strlen(tname))) {
+            return&trace_list[i];
Quoted 9 lines

simpletrace.c void do_info_trace(Monitor *mon) · line 129

Quoted 3 lines
simpletrace.c +3 -0
+        }
+    }
+    return NULL; /* indicates end of list reached without a match */

I don't see the need for hashing. We don't actually have a hash table and are doing a linear search. There will be a smallish constant number of trace events and only change_tracepoint_by_name() needs to do a lookup. There's no need to optimize that.

I was only optimising lookups. Instead of doing a strlen()-size comparison for each tracepoint, we end up doing a constant int-size comparison till there is possibility of hash collision. Strlen()-size lookups isnt really an issure right now, but will be more glaring if qemu ends up having a much larger number of tracepoints.

Quoted 4 lines

Is strncmp() used so looking up "foo" is like searching for foo*? The strlen(tname) should be outside the loop. I think that could be useful but we should document it or just use strcmp().

The hash would take care of most such cases. But this might be an issue in the rare event of a hash collision happening when foo_1 and foo_2 match as well. I'll fix this.

Quoted 46 lines
Quoted 46 lines
simpletrace.c +11 -0
+}
+
+void change_tracepoint_state(const char *tname, bool tstate)
+{
+    Tracepoint *tp;
+
+    tp = find_tracepoint_by_name(tname);
+    if (tp) {
+        tp->state = tstate;
+    }
+}
tracetool +9 -1
index 2c73bab..00af205 100755
@@ -123,14 +123,20 @@ linetoc_end_nop()
  linetoh_begin_simple()
  {
      cat<<EOF
+#include<stdbool.h>
+
  typedef unsigned int TraceEvent;
 
+void init_tracepoint_table(void);
+void init_tracepoint(const char *tname, TraceEvent tevent);
  void trace1(TraceEvent event, unsigned long x1);
  void trace2(TraceEvent event, unsigned long x1, unsigned long x2);
  void trace3(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3);
  void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4);
  void trace5(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4, unsigned long x5);
  void do_info_trace(Monitor *mon);
+void do_info_all_tracepoints(Monitor *mon);
+void change_tracepoint_state(const char *tname, bool tstate);
  EOF
 
      simple_event_num=0
@@ -163,22 +169,38 @@ EOF
 
  linetoh_end_simple()
  {
-    return
+    cat<<EOF
+#define NR_TRACEPOINTS $simple_event_num
+EOF
  }
 
Quoted 12 lines

tracetool EOF · line 183

Quoted 8 lines
tracetool +5 -1
  linetoc_begin_simple()
  {
-    return
+    cat<<EOF
+#include "trace.h"
+
+void init_tracepoint_table(void) {
+EOF

Have you looked at module.h? Perhaps that provides a good solution for initializing trace events. It would allow the vl.c changes to be done without an #ifdef.

Thanks for the tip, I'll check.

Quoted 16 lines
Quoted 16 lines
tracetool +8 -1
+    simple_event_num=0
+
  }
 
  linetoc_simple()
  {
-    return
+    local name
+    name=$(get_name "$1")
+    cat<<EOF
+init_tracepoint("$name", $simple_event_num);
+EOF
+    simple_event_num=$((simple_event_num + 1))
  }
 
Quoted 8 lines

tracetool EOF · line 201

Quoted 5 lines
tracetool +2 -1
  linetoc_end_simple()
  {
-    return
+    cat<<EOF
+return;

Please don't use return at the end of a void function. Coding style.

Ok.

Quoted 4 lines
Quoted 4 lines
tracetool +2 -0
+}
+EOF
...
Signature · 5 lines
Prerna Saxena

Linux Technology Centre,
IBM Systems and Technology Lab,
Bangalore, India
#9 lore raw

Re: [Qemu-devel] Re: [PATCH 3/3] Toggle tracepoint state

Quoted 28 lines · Prerna Saxena
Quoted 17 lines · Stefan Hajnoczi
Quoted 17 lines
simpletrace.c +15 -0
+}
+
+static Tracepoint* find_tracepoint_by_name(const char *tname)
+{
+    unsigned int i, name_hash;
+
+    if (!tname) {
+        return NULL;
+    }
+
+    name_hash = qemu_hash(tname);
+
+    for (i=0; i<NR_TRACEPOINTS; i++) {
+        if (trace_list[i].hash == name_hash&&
+                       !strncmp(trace_list[i].tp_name, tname,

strlen(tname))) {
+            return&trace_list[i];

Quoted 9 lines
Quoted 3 lines

+        }
+    }
+    return NULL; /* indicates end of list reached without a match */

I don't see the need for hashing.  We don't actually have a hash table and are doing a linear search.  There will be a smallish constant number of trace events and only change_tracepoint_by_name() needs to do a lookup.  There's no need to optimize that.

Quoted 5 lines

I was only optimising lookups. Instead of doing a strlen()-size comparison for each tracepoint, we end up doing a constant int-size comparison till there is possibility of hash collision. Strlen()-size lookups isnt really an issure right now, but will be more glaring if qemu ends up having a much larger number of tracepoints.

The thing is we only need to do lookups when tracepoints are enabled/disabled. That is a very rare operation, not critical path.

This feels like premature optimization. If there is a performance bottleneck down the road we can switch to a data structure that has better time complexity than an array. This is an easy change to make later because it will not affect the user interface.

In the meantime linear strcmp() over an array keeps the code simple. We can drop the tdb_hash() patch and remove some lines from this patch.

Quoted 51 lines
Quoted 38 lines
Quoted 38 lines
tracetool +0 -0
index 2c73bab..00af205 100755
@@ -123,14 +123,20 @@ linetoc_end_nop()
  linetoh_begin_simple()
  {

     cat<<EOF

tracetool +2 -0
+#include<stdbool.h>
+

 typedef unsigned int TraceEvent;

tracetool +2 -0
+void init_tracepoint_table(void);
+void init_tracepoint(const char *tname, TraceEvent tevent);

 void trace1(TraceEvent event, unsigned long x1);
 void trace2(TraceEvent event, unsigned long x1, unsigned long x2); void trace3(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3);
 void trace4(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4);
 void trace5(TraceEvent event, unsigned long x1, unsigned long x2, unsigned long x3, unsigned long x4, unsigned long x5); void do_info_trace(Monitor *mon);

tracetool +2 -0
+void do_info_all_tracepoints(Monitor *mon);
+void change_tracepoint_state(const char *tname, bool tstate);

 EOF

     simple_event_num=0

tracetool +3 -1
@@ -163,22 +169,38 @@ EOF
 
  linetoh_end_simple()
  {
-    return
+    cat<<EOF
+#define NR_TRACEPOINTS $simple_event_num
+EOF
  }
 
Quoted 12 lines

tracetool EOF · line 183

Quoted 8 lines
tracetool +5 -1
  linetoc_begin_simple()
  {
-    return
+    cat<<EOF
+#include "trace.h"
+
+void init_tracepoint_table(void) {
+EOF

Have you looked at module.h?  Perhaps that provides a good solution for initializing trace events.  It would allow the vl.c changes to be done without an #ifdef.

Quoted 1 line

Thanks for the tip, I'll check.

I also wonder if it's possible to statically initialize the TraceEvent array in the generated trace.c. That way we need no vl.c startup magic and tracing is available even in the very early stages of QEMU startup. What do you think?

Stefan

#10 lore raw
In reply to Prerna Saxena
Quoted 6 lines · Prerna Saxena

This is v3 of a set of patches to enable trace visualization & control via the QEMU monitor. It is based on trace infrastructure posted by Stefan :
( http://lists.gnu.org/archive/html/qemu-devel/2010-05/msg02407.html )

Quoted 9 lines

This patchset adds monitor commands :
- info trace : to view current contents of the trace buffer
- info tracepoints : to view all available tracepoints and their state.
- tracepoint NAME on|off : to enable/disable the logging of data from tracepoint 'NAME' to on/off.

Changelog :
- Clean-ups from v2, particularly relating to export of tdb_hash()

But it's still using the legacy monitor command interface. See [1]+[2] for a mechanism that allows you to convert to the new interface without automatically exposing your commands via QMP.

Jan

[1] http://thread.gmane.org/gmane.comp.emulators.qemu/74356
[2] http://thread.gmane.org/gmane.comp.emulators.qemu/74372

Signature · 2 lines
Siemens AG, Corporate Technology, CT T DE IT 1
Corporate Competence Center Embedded Linux