i2c: Add message transfer tracepoints for SMBUS [ver #2]
authorDavid Howells <dhowells@redhat.com>
Thu, 6 Mar 2014 13:36:06 +0000 (13:36 +0000)
committerWolfram Sang <wsa@the-dreams.de>
Thu, 13 Mar 2014 21:15:07 +0000 (22:15 +0100)
The SMBUS tracepoints can be enabled thusly:

echo 1 >/sys/kernel/debug/tracing/events/i2c/enable

and will dump messages that can be viewed in /sys/kernel/debug/tracing/trace
that look like:

         ... smbus_read: i2c-0 a=051 f=0000 c=fa BYTE_DATA
         ... smbus_reply: i2c-0 a=051 f=0000 c=fa BYTE_DATA l=1 [39]
         ... smbus_result: i2c-0 a=051 f=0000 c=fa BYTE_DATA rd res=0

formatted as:

i2c-<adapter-nr>
a=<addr>
f=<flags>
c=<command>
<protocol-name>
<rd|wr>
res=<result>
l=<data-len>
[<data-block>]

The adapters to be traced can be selected by something like:

echo adapter_nr==1 >/sys/kernel/debug/tracing/events/i2c/filter

Note that this shares the same filter and enablement as i2c.

Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
drivers/i2c/i2c-core.c
include/trace/events/i2c.h

index bdedbee..7c7f4b8 100644 (file)
@@ -2565,6 +2565,14 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
        int try;
        s32 res;
 
+       /* If enabled, the following two tracepoints are conditional on
+        * read_write and protocol.
+        */
+       trace_smbus_write(adapter, addr, flags, read_write,
+                         command, protocol, data);
+       trace_smbus_read(adapter, addr, flags, read_write,
+                        command, protocol);
+
        flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;
 
        if (adapter->algo->smbus_xfer) {
@@ -2585,15 +2593,24 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
                i2c_unlock_adapter(adapter);
 
                if (res != -EOPNOTSUPP || !adapter->algo->master_xfer)
-                       return res;
+                       goto trace;
                /*
                 * Fall back to i2c_smbus_xfer_emulated if the adapter doesn't
                 * implement native support for the SMBus operation.
                 */
        }
 
-       return i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
-                                      command, protocol, data);
+       res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
+                                     command, protocol, data);
+
+trace:
+       /* If enabled, the reply tracepoint is conditional on read_write. */
+       trace_smbus_reply(adapter, addr, flags, read_write,
+                         command, protocol, data);
+       trace_smbus_result(adapter, addr, flags, read_write,
+                          command, protocol, res);
+
+       return res;
 }
 EXPORT_SYMBOL(i2c_smbus_xfer);
 
index 4800207..fe17187 100644 (file)
@@ -1,4 +1,4 @@
-/* I2C message transfer tracepoints
+/* I2C and SMBUS message transfer tracepoints
  *
  * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
@@ -144,6 +144,228 @@ TRACE_EVENT_FN(i2c_result,
               i2c_transfer_trace_reg,
               i2c_transfer_trace_unreg);
 
+/*
+ * i2c_smbus_xfer() write data or procedure call request
+ */
+TRACE_EVENT_CONDITION(smbus_write,
+       TP_PROTO(const struct i2c_adapter *adap,
+                u16 addr, unsigned short flags,
+                char read_write, u8 command, int protocol,
+                const union i2c_smbus_data *data),
+       TP_ARGS(adap, addr, flags, read_write, command, protocol, data),
+       TP_CONDITION(read_write == I2C_SMBUS_WRITE ||
+                    protocol == I2C_SMBUS_PROC_CALL ||
+                    protocol == I2C_SMBUS_BLOCK_PROC_CALL),
+       TP_STRUCT__entry(
+               __field(int,    adapter_nr              )
+               __field(__u16,  addr                    )
+               __field(__u16,  flags                   )
+               __field(__u8,   command                 )
+               __field(__u8,   len                     )
+               __field(__u32,  protocol                )
+               __array(__u8, buf, I2C_SMBUS_BLOCK_MAX + 2)     ),
+       TP_fast_assign(
+               __entry->adapter_nr = adap->nr;
+               __entry->addr = addr;
+               __entry->flags = flags;
+               __entry->command = command;
+               __entry->protocol = protocol;
+
+               switch (protocol) {
+               case I2C_SMBUS_BYTE_DATA:
+                       __entry->len = 1;
+                       goto copy;
+               case I2C_SMBUS_WORD_DATA:
+               case I2C_SMBUS_PROC_CALL:
+                       __entry->len = 2;
+                       goto copy;
+               case I2C_SMBUS_BLOCK_DATA:
+               case I2C_SMBUS_BLOCK_PROC_CALL:
+               case I2C_SMBUS_I2C_BLOCK_DATA:
+                       __entry->len = data->block[0] + 1;
+               copy:
+                       memcpy(__entry->buf, data->block, __entry->len);
+                       break;
+               case I2C_SMBUS_QUICK:
+               case I2C_SMBUS_BYTE:
+               case I2C_SMBUS_I2C_BLOCK_BROKEN:
+               default:
+                       __entry->len = 0;
+               }
+                      ),
+       TP_printk("i2c-%d a=%03x f=%04x c=%x %s l=%u [%*phD]",
+                 __entry->adapter_nr,
+                 __entry->addr,
+                 __entry->flags,
+                 __entry->command,
+                 __print_symbolic(__entry->protocol,
+                                  { I2C_SMBUS_QUICK,           "QUICK" },
+                                  { I2C_SMBUS_BYTE,            "BYTE"  },
+                                  { I2C_SMBUS_BYTE_DATA,               "BYTE_DATA" },
+                                  { I2C_SMBUS_WORD_DATA,               "WORD_DATA" },
+                                  { I2C_SMBUS_PROC_CALL,               "PROC_CALL" },
+                                  { I2C_SMBUS_BLOCK_DATA,              "BLOCK_DATA" },
+                                  { I2C_SMBUS_I2C_BLOCK_BROKEN,        "I2C_BLOCK_BROKEN" },
+                                  { I2C_SMBUS_BLOCK_PROC_CALL, "BLOCK_PROC_CALL" },
+                                  { I2C_SMBUS_I2C_BLOCK_DATA,  "I2C_BLOCK_DATA" }),
+                 __entry->len,
+                 __entry->len, __entry->buf
+                 ));
+
+/*
+ * i2c_smbus_xfer() read data request
+ */
+TRACE_EVENT_CONDITION(smbus_read,
+       TP_PROTO(const struct i2c_adapter *adap,
+                u16 addr, unsigned short flags,
+                char read_write, u8 command, int protocol),
+       TP_ARGS(adap, addr, flags, read_write, command, protocol),
+       TP_CONDITION(!(read_write == I2C_SMBUS_WRITE ||
+                      protocol == I2C_SMBUS_PROC_CALL ||
+                      protocol == I2C_SMBUS_BLOCK_PROC_CALL)),
+       TP_STRUCT__entry(
+               __field(int,    adapter_nr              )
+               __field(__u16,  flags                   )
+               __field(__u16,  addr                    )
+               __field(__u8,   command                 )
+               __field(__u32,  protocol                )
+               __array(__u8, buf, I2C_SMBUS_BLOCK_MAX + 2)     ),
+       TP_fast_assign(
+               __entry->adapter_nr = adap->nr;
+               __entry->addr = addr;
+               __entry->flags = flags;
+               __entry->command = command;
+               __entry->protocol = protocol;
+                      ),
+       TP_printk("i2c-%d a=%03x f=%04x c=%x %s",
+                 __entry->adapter_nr,
+                 __entry->addr,
+                 __entry->flags,
+                 __entry->command,
+                 __print_symbolic(__entry->protocol,
+                                  { I2C_SMBUS_QUICK,           "QUICK" },
+                                  { I2C_SMBUS_BYTE,            "BYTE"  },
+                                  { I2C_SMBUS_BYTE_DATA,               "BYTE_DATA" },
+                                  { I2C_SMBUS_WORD_DATA,               "WORD_DATA" },
+                                  { I2C_SMBUS_PROC_CALL,               "PROC_CALL" },
+                                  { I2C_SMBUS_BLOCK_DATA,              "BLOCK_DATA" },
+                                  { I2C_SMBUS_I2C_BLOCK_BROKEN,        "I2C_BLOCK_BROKEN" },
+                                  { I2C_SMBUS_BLOCK_PROC_CALL, "BLOCK_PROC_CALL" },
+                                  { I2C_SMBUS_I2C_BLOCK_DATA,  "I2C_BLOCK_DATA" })
+                 ));
+
+/*
+ * i2c_smbus_xfer() read data or procedure call reply
+ */
+TRACE_EVENT_CONDITION(smbus_reply,
+       TP_PROTO(const struct i2c_adapter *adap,
+                u16 addr, unsigned short flags,
+                char read_write, u8 command, int protocol,
+                const union i2c_smbus_data *data),
+       TP_ARGS(adap, addr, flags, read_write, command, protocol, data),
+       TP_CONDITION(read_write == I2C_SMBUS_READ),
+       TP_STRUCT__entry(
+               __field(int,    adapter_nr              )
+               __field(__u16,  addr                    )
+               __field(__u16,  flags                   )
+               __field(__u8,   command                 )
+               __field(__u8,   len                     )
+               __field(__u32,  protocol                )
+               __array(__u8, buf, I2C_SMBUS_BLOCK_MAX + 2)     ),
+       TP_fast_assign(
+               __entry->adapter_nr = adap->nr;
+               __entry->addr = addr;
+               __entry->flags = flags;
+               __entry->command = command;
+               __entry->protocol = protocol;
+
+               switch (protocol) {
+               case I2C_SMBUS_BYTE:
+               case I2C_SMBUS_BYTE_DATA:
+                       __entry->len = 1;
+                       goto copy;
+               case I2C_SMBUS_WORD_DATA:
+               case I2C_SMBUS_PROC_CALL:
+                       __entry->len = 2;
+                       goto copy;
+               case I2C_SMBUS_BLOCK_DATA:
+               case I2C_SMBUS_BLOCK_PROC_CALL:
+               case I2C_SMBUS_I2C_BLOCK_DATA:
+                       __entry->len = data->block[0] + 1;
+               copy:
+                       memcpy(__entry->buf, data->block, __entry->len);
+                       break;
+               case I2C_SMBUS_QUICK:
+               case I2C_SMBUS_I2C_BLOCK_BROKEN:
+               default:
+                       __entry->len = 0;
+               }
+                      ),
+       TP_printk("i2c-%d a=%03x f=%04x c=%x %s l=%u [%*phD]",
+                 __entry->adapter_nr,
+                 __entry->addr,
+                 __entry->flags,
+                 __entry->command,
+                 __print_symbolic(__entry->protocol,
+                                  { I2C_SMBUS_QUICK,           "QUICK" },
+                                  { I2C_SMBUS_BYTE,            "BYTE"  },
+                                  { I2C_SMBUS_BYTE_DATA,               "BYTE_DATA" },
+                                  { I2C_SMBUS_WORD_DATA,               "WORD_DATA" },
+                                  { I2C_SMBUS_PROC_CALL,               "PROC_CALL" },
+                                  { I2C_SMBUS_BLOCK_DATA,              "BLOCK_DATA" },
+                                  { I2C_SMBUS_I2C_BLOCK_BROKEN,        "I2C_BLOCK_BROKEN" },
+                                  { I2C_SMBUS_BLOCK_PROC_CALL, "BLOCK_PROC_CALL" },
+                                  { I2C_SMBUS_I2C_BLOCK_DATA,  "I2C_BLOCK_DATA" }),
+                 __entry->len,
+                 __entry->len, __entry->buf
+                 ));
+
+/*
+ * i2c_smbus_xfer() result
+ */
+TRACE_EVENT(smbus_result,
+           TP_PROTO(const struct i2c_adapter *adap,
+                    u16 addr, unsigned short flags,
+                    char read_write, u8 command, int protocol,
+                    int res),
+           TP_ARGS(adap, addr, flags, read_write, command, protocol, res),
+           TP_STRUCT__entry(
+                   __field(int,        adapter_nr              )
+                   __field(__u16,      addr                    )
+                   __field(__u16,      flags                   )
+                   __field(__u8,       read_write              )
+                   __field(__u8,       command                 )
+                   __field(__s16,      res                     )
+                   __field(__u32,      protocol                )
+                            ),
+           TP_fast_assign(
+                   __entry->adapter_nr = adap->nr;
+                   __entry->addr = addr;
+                   __entry->flags = flags;
+                   __entry->read_write = read_write;
+                   __entry->command = command;
+                   __entry->protocol = protocol;
+                   __entry->res = res;
+                          ),
+           TP_printk("i2c-%d a=%03x f=%04x c=%x %s %s res=%d",
+                     __entry->adapter_nr,
+                     __entry->addr,
+                     __entry->flags,
+                     __entry->command,
+                     __print_symbolic(__entry->protocol,
+                                      { I2C_SMBUS_QUICK,               "QUICK" },
+                                      { I2C_SMBUS_BYTE,                "BYTE"  },
+                                      { I2C_SMBUS_BYTE_DATA,           "BYTE_DATA" },
+                                      { I2C_SMBUS_WORD_DATA,           "WORD_DATA" },
+                                      { I2C_SMBUS_PROC_CALL,           "PROC_CALL" },
+                                      { I2C_SMBUS_BLOCK_DATA,          "BLOCK_DATA" },
+                                      { I2C_SMBUS_I2C_BLOCK_BROKEN,    "I2C_BLOCK_BROKEN" },
+                                      { I2C_SMBUS_BLOCK_PROC_CALL,     "BLOCK_PROC_CALL" },
+                                      { I2C_SMBUS_I2C_BLOCK_DATA,      "I2C_BLOCK_DATA" }),
+                     __entry->read_write == I2C_SMBUS_WRITE ? "wr" : "rd",
+                     __entry->res
+                     ));
+
 #endif /* _TRACE_I2C_H */
 
 /* This part must be outside protection */