| /* |
| * Virtio 9p PDU debug |
| * |
| * Copyright IBM, Corp. 2010 |
| * |
| * Authors: |
| * Anthony Liguori <aliguori@us.ibm.com> |
| * |
| * This work is licensed under the terms of the GNU GPL, version 2. See |
| * the COPYING file in the top-level directory. |
| * |
| */ |
| #include "virtio.h" |
| #include "pc.h" |
| #include "virtio-9p.h" |
| #include "virtio-9p-debug.h" |
| |
| #define BUG_ON(cond) assert(!(cond)) |
| |
| static FILE *llogfile; |
| |
| static struct iovec *get_sg(V9fsPDU *pdu, int rx) |
| { |
| if (rx) { |
| return pdu->elem.in_sg; |
| } |
| return pdu->elem.out_sg; |
| } |
| |
| static int get_sg_count(V9fsPDU *pdu, int rx) |
| { |
| if (rx) { |
| return pdu->elem.in_num; |
| } |
| return pdu->elem.out_num; |
| |
| } |
| |
| static void pprint_int8(V9fsPDU *pdu, int rx, size_t *offsetp, |
| const char *name) |
| { |
| size_t copied; |
| int count = get_sg_count(pdu, rx); |
| size_t offset = *offsetp; |
| struct iovec *sg = get_sg(pdu, rx); |
| int8_t value; |
| |
| copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value)); |
| |
| BUG_ON(copied != sizeof(value)); |
| offset += sizeof(value); |
| fprintf(llogfile, "%s=0x%x", name, value); |
| *offsetp = offset; |
| } |
| |
| static void pprint_int16(V9fsPDU *pdu, int rx, size_t *offsetp, |
| const char *name) |
| { |
| size_t copied; |
| int count = get_sg_count(pdu, rx); |
| struct iovec *sg = get_sg(pdu, rx); |
| size_t offset = *offsetp; |
| int16_t value; |
| |
| |
| copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value)); |
| |
| BUG_ON(copied != sizeof(value)); |
| offset += sizeof(value); |
| fprintf(llogfile, "%s=0x%x", name, value); |
| *offsetp = offset; |
| } |
| |
| static void pprint_int32(V9fsPDU *pdu, int rx, size_t *offsetp, |
| const char *name) |
| { |
| size_t copied; |
| int count = get_sg_count(pdu, rx); |
| struct iovec *sg = get_sg(pdu, rx); |
| size_t offset = *offsetp; |
| int32_t value; |
| |
| |
| copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value)); |
| |
| BUG_ON(copied != sizeof(value)); |
| offset += sizeof(value); |
| fprintf(llogfile, "%s=0x%x", name, value); |
| *offsetp = offset; |
| } |
| |
| static void pprint_int64(V9fsPDU *pdu, int rx, size_t *offsetp, |
| const char *name) |
| { |
| size_t copied; |
| int count = get_sg_count(pdu, rx); |
| struct iovec *sg = get_sg(pdu, rx); |
| size_t offset = *offsetp; |
| int64_t value; |
| |
| |
| copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value)); |
| |
| BUG_ON(copied != sizeof(value)); |
| offset += sizeof(value); |
| fprintf(llogfile, "%s=0x%" PRIx64, name, value); |
| *offsetp = offset; |
| } |
| |
| static void pprint_str(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) |
| { |
| int sg_count = get_sg_count(pdu, rx); |
| struct iovec *sg = get_sg(pdu, rx); |
| size_t offset = *offsetp; |
| uint16_t tmp_size, size; |
| size_t result; |
| size_t copied = 0; |
| int i = 0; |
| |
| /* get the size */ |
| copied = do_pdu_unpack(&tmp_size, sg, sg_count, offset, sizeof(tmp_size)); |
| BUG_ON(copied != sizeof(tmp_size)); |
| size = le16_to_cpupu(&tmp_size); |
| offset += copied; |
| |
| fprintf(llogfile, "%s=", name); |
| for (i = 0; size && i < sg_count; i++) { |
| size_t len; |
| if (offset >= sg[i].iov_len) { |
| /* skip this sg */ |
| offset -= sg[i].iov_len; |
| continue; |
| } else { |
| len = MIN(sg[i].iov_len - offset, size); |
| result = fwrite(sg[i].iov_base + offset, 1, len, llogfile); |
| BUG_ON(result != len); |
| size -= len; |
| copied += len; |
| if (size) { |
| offset = 0; |
| continue; |
| } |
| } |
| } |
| *offsetp += copied; |
| } |
| |
| static void pprint_qid(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) |
| { |
| fprintf(llogfile, "%s={", name); |
| pprint_int8(pdu, rx, offsetp, "type"); |
| pprint_int32(pdu, rx, offsetp, ", version"); |
| pprint_int64(pdu, rx, offsetp, ", path"); |
| fprintf(llogfile, "}"); |
| } |
| |
| static void pprint_stat(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) |
| { |
| fprintf(llogfile, "%s={", name); |
| pprint_int16(pdu, rx, offsetp, "size"); |
| pprint_int16(pdu, rx, offsetp, ", type"); |
| pprint_int32(pdu, rx, offsetp, ", dev"); |
| pprint_qid(pdu, rx, offsetp, ", qid"); |
| pprint_int32(pdu, rx, offsetp, ", mode"); |
| pprint_int32(pdu, rx, offsetp, ", atime"); |
| pprint_int32(pdu, rx, offsetp, ", mtime"); |
| pprint_int64(pdu, rx, offsetp, ", length"); |
| pprint_str(pdu, rx, offsetp, ", name"); |
| pprint_str(pdu, rx, offsetp, ", uid"); |
| pprint_str(pdu, rx, offsetp, ", gid"); |
| pprint_str(pdu, rx, offsetp, ", muid"); |
| if (dotu) { |
| pprint_str(pdu, rx, offsetp, ", extension"); |
| pprint_int32(pdu, rx, offsetp, ", uid"); |
| pprint_int32(pdu, rx, offsetp, ", gid"); |
| pprint_int32(pdu, rx, offsetp, ", muid"); |
| } |
| fprintf(llogfile, "}"); |
| } |
| |
| static void pprint_strs(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) |
| { |
| int sg_count = get_sg_count(pdu, rx); |
| struct iovec *sg = get_sg(pdu, rx); |
| size_t offset = *offsetp; |
| uint16_t tmp_count, count, i; |
| size_t copied = 0; |
| |
| fprintf(llogfile, "%s={", name); |
| |
| /* Get the count */ |
| copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count)); |
| BUG_ON(copied != sizeof(tmp_count)); |
| count = le16_to_cpupu(&tmp_count); |
| offset += copied; |
| |
| for (i = 0; i < count; i++) { |
| char str[512]; |
| if (i) { |
| fprintf(llogfile, ", "); |
| } |
| snprintf(str, sizeof(str), "[%d]", i); |
| pprint_str(pdu, rx, &offset, str); |
| } |
| |
| fprintf(llogfile, "}"); |
| |
| *offsetp = offset; |
| } |
| |
| static void pprint_qids(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) |
| { |
| int sg_count = get_sg_count(pdu, rx); |
| struct iovec *sg = get_sg(pdu, rx); |
| size_t offset = *offsetp; |
| uint16_t tmp_count, count, i; |
| size_t copied = 0; |
| |
| fprintf(llogfile, "%s={", name); |
| |
| copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count)); |
| BUG_ON(copied != sizeof(tmp_count)); |
| count = le16_to_cpupu(&tmp_count); |
| offset += copied; |
| |
| for (i = 0; i < count; i++) { |
| char str[512]; |
| if (i) { |
| fprintf(llogfile, ", "); |
| } |
| snprintf(str, sizeof(str), "[%d]", i); |
| pprint_qid(pdu, rx, &offset, str); |
| } |
| |
| fprintf(llogfile, "}"); |
| |
| *offsetp = offset; |
| } |
| |
| static void pprint_sg(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) |
| { |
| struct iovec *sg = get_sg(pdu, rx); |
| unsigned int count; |
| int i; |
| |
| if (rx) { |
| count = pdu->elem.in_num; |
| } else { |
| count = pdu->elem.out_num; |
| } |
| |
| fprintf(llogfile, "%s={", name); |
| for (i = 0; i < count; i++) { |
| if (i) { |
| fprintf(llogfile, ", "); |
| } |
| fprintf(llogfile, "(%p, 0x%zx)", sg[i].iov_base, sg[i].iov_len); |
| } |
| fprintf(llogfile, "}"); |
| } |
| |
| /* FIXME: read from a directory fid returns serialized stat_t's */ |
| #ifdef DEBUG_DATA |
| static void pprint_data(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) |
| { |
| struct iovec *sg = get_sg(pdu, rx); |
| size_t offset = *offsetp; |
| unsigned int count; |
| int32_t size; |
| int total, i, j; |
| ssize_t len; |
| |
| if (rx) { |
| count = pdu->elem.in_num; |
| } else |
| count = pdu->elem.out_num; |
| } |
| |
| BUG_ON((offset + sizeof(size)) > sg[0].iov_len); |
| |
| memcpy(&size, sg[0].iov_base + offset, sizeof(size)); |
| offset += sizeof(size); |
| |
| fprintf(llogfile, "size: %x\n", size); |
| |
| sg[0].iov_base += 11; /* skip header */ |
| sg[0].iov_len -= 11; |
| |
| total = 0; |
| for (i = 0; i < count; i++) { |
| total += sg[i].iov_len; |
| if (total >= size) { |
| /* trim sg list so writev does the right thing */ |
| sg[i].iov_len -= (total - size); |
| i++; |
| break; |
| } |
| } |
| |
| fprintf(llogfile, "%s={\"", name); |
| fflush(llogfile); |
| for (j = 0; j < i; j++) { |
| if (j) { |
| fprintf(llogfile, "\", \""); |
| fflush(llogfile); |
| } |
| |
| do { |
| len = writev(fileno(llogfile), &sg[j], 1); |
| } while (len == -1 && errno == EINTR); |
| fprintf(llogfile, "len == %ld: %m\n", len); |
| BUG_ON(len != sg[j].iov_len); |
| } |
| fprintf(llogfile, "\"}"); |
| |
| sg[0].iov_base -= 11; |
| sg[0].iov_len += 11; |
| |
| } |
| #endif |
| |
| void pprint_pdu(V9fsPDU *pdu) |
| { |
| size_t offset = 7; |
| |
| if (llogfile == NULL) { |
| llogfile = fopen("/tmp/pdu.log", "w"); |
| } |
| |
| switch (pdu->id) { |
| case P9_TVERSION: |
| fprintf(llogfile, "TVERSION: ("); |
| pprint_int32(pdu, 0, &offset, "msize"); |
| pprint_str(pdu, 0, &offset, ", version"); |
| break; |
| case P9_RVERSION: |
| fprintf(llogfile, "RVERSION: ("); |
| pprint_int32(pdu, 1, &offset, "msize"); |
| pprint_str(pdu, 1, &offset, ", version"); |
| break; |
| case P9_TAUTH: |
| fprintf(llogfile, "TAUTH: ("); |
| pprint_int32(pdu, 0, &offset, "afid"); |
| pprint_str(pdu, 0, &offset, ", uname"); |
| pprint_str(pdu, 0, &offset, ", aname"); |
| if (dotu) { |
| pprint_int32(pdu, 0, &offset, ", n_uname"); |
| } |
| break; |
| case P9_RAUTH: |
| fprintf(llogfile, "RAUTH: ("); |
| pprint_qid(pdu, 1, &offset, "qid"); |
| break; |
| case P9_TATTACH: |
| fprintf(llogfile, "TATTACH: ("); |
| pprint_int32(pdu, 0, &offset, "fid"); |
| pprint_int32(pdu, 0, &offset, ", afid"); |
| pprint_str(pdu, 0, &offset, ", uname"); |
| pprint_str(pdu, 0, &offset, ", aname"); |
| if (dotu) { |
| pprint_int32(pdu, 0, &offset, ", n_uname"); |
| } |
| break; |
| case P9_RATTACH: |
| fprintf(llogfile, "RATTACH: ("); |
| pprint_qid(pdu, 1, &offset, "qid"); |
| break; |
| case P9_TERROR: |
| fprintf(llogfile, "TERROR: ("); |
| break; |
| case P9_RERROR: |
| fprintf(llogfile, "RERROR: ("); |
| pprint_str(pdu, 1, &offset, "ename"); |
| if (dotu) { |
| pprint_int32(pdu, 1, &offset, ", ecode"); |
| } |
| break; |
| case P9_TFLUSH: |
| fprintf(llogfile, "TFLUSH: ("); |
| pprint_int16(pdu, 0, &offset, "oldtag"); |
| break; |
| case P9_RFLUSH: |
| fprintf(llogfile, "RFLUSH: ("); |
| break; |
| case P9_TWALK: |
| fprintf(llogfile, "TWALK: ("); |
| pprint_int32(pdu, 0, &offset, "fid"); |
| pprint_int32(pdu, 0, &offset, ", newfid"); |
| pprint_strs(pdu, 0, &offset, ", wnames"); |
| break; |
| case P9_RWALK: |
| fprintf(llogfile, "RWALK: ("); |
| pprint_qids(pdu, 1, &offset, "wqids"); |
| break; |
| case P9_TOPEN: |
| fprintf(llogfile, "TOPEN: ("); |
| pprint_int32(pdu, 0, &offset, "fid"); |
| pprint_int8(pdu, 0, &offset, ", mode"); |
| break; |
| case P9_ROPEN: |
| fprintf(llogfile, "ROPEN: ("); |
| pprint_qid(pdu, 1, &offset, "qid"); |
| pprint_int32(pdu, 1, &offset, ", iounit"); |
| break; |
| case P9_TCREATE: |
| fprintf(llogfile, "TCREATE: ("); |
| pprint_int32(pdu, 0, &offset, "fid"); |
| pprint_str(pdu, 0, &offset, ", name"); |
| pprint_int32(pdu, 0, &offset, ", perm"); |
| pprint_int8(pdu, 0, &offset, ", mode"); |
| if (dotu) { |
| pprint_str(pdu, 0, &offset, ", extension"); |
| } |
| break; |
| case P9_RCREATE: |
| fprintf(llogfile, "RCREATE: ("); |
| pprint_qid(pdu, 1, &offset, "qid"); |
| pprint_int32(pdu, 1, &offset, ", iounit"); |
| break; |
| case P9_TREAD: |
| fprintf(llogfile, "TREAD: ("); |
| pprint_int32(pdu, 0, &offset, "fid"); |
| pprint_int64(pdu, 0, &offset, ", offset"); |
| pprint_int32(pdu, 0, &offset, ", count"); |
| pprint_sg(pdu, 0, &offset, ", sg"); |
| break; |
| case P9_RREAD: |
| fprintf(llogfile, "RREAD: ("); |
| pprint_int32(pdu, 1, &offset, "count"); |
| pprint_sg(pdu, 1, &offset, ", sg"); |
| offset = 7; |
| #ifdef DEBUG_DATA |
| pprint_data(pdu, 1, &offset, ", data"); |
| #endif |
| break; |
| case P9_TWRITE: |
| fprintf(llogfile, "TWRITE: ("); |
| pprint_int32(pdu, 0, &offset, "fid"); |
| pprint_int64(pdu, 0, &offset, ", offset"); |
| pprint_int32(pdu, 0, &offset, ", count"); |
| break; |
| case P9_RWRITE: |
| fprintf(llogfile, "RWRITE: ("); |
| pprint_int32(pdu, 1, &offset, "count"); |
| break; |
| case P9_TCLUNK: |
| fprintf(llogfile, "TCLUNK: ("); |
| pprint_int32(pdu, 0, &offset, "fid"); |
| break; |
| case P9_RCLUNK: |
| fprintf(llogfile, "RCLUNK: ("); |
| break; |
| case P9_TREMOVE: |
| fprintf(llogfile, "TREMOVE: ("); |
| pprint_int32(pdu, 0, &offset, "fid"); |
| break; |
| case P9_RREMOVE: |
| fprintf(llogfile, "RREMOVE: ("); |
| break; |
| case P9_TSTAT: |
| fprintf(llogfile, "TSTAT: ("); |
| pprint_int32(pdu, 0, &offset, "fid"); |
| break; |
| case P9_RSTAT: |
| fprintf(llogfile, "RSTAT: ("); |
| offset += 2; /* ignored */ |
| pprint_stat(pdu, 1, &offset, "stat"); |
| break; |
| case P9_TWSTAT: |
| fprintf(llogfile, "TWSTAT: ("); |
| pprint_int32(pdu, 0, &offset, "fid"); |
| offset += 2; /* ignored */ |
| pprint_stat(pdu, 0, &offset, ", stat"); |
| break; |
| case P9_RWSTAT: |
| fprintf(llogfile, "RWSTAT: ("); |
| break; |
| default: |
| fprintf(llogfile, "unknown(%d): (", pdu->id); |
| break; |
| } |
| |
| fprintf(llogfile, ")\n"); |
| } |