diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index c9f6bfc..ab08b96 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -66,6 +66,7 @@
 
 /* Global statistics */
 static uint32_t rsc_chain_no_mem;
+static uint64_t virtio_net_received;
 
 /* Switcher to enable/disable rsc */
 static bool virtio_net_rsc_bypass;
@@ -1679,10 +1680,12 @@ static void virtio_net_rsc_purge(void *opq)
 
         if (ret == 0) {
             /* Try next queue */
+            chain->stat.purge_failed++;
             continue;
         }
     }
 
+    chain->stat.timer++;
     if (!QTAILQ_EMPTY(&chain->buffers)) {
         timer_mod(chain->drain_timer,
                   qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + rsc_timeout);
@@ -1715,6 +1718,7 @@ static int virtio_net_rsc_cache_buf(NetRscChain *chain, NetClientState *nc,
 
     seg = g_malloc(sizeof(NetRscSeg));
     if (!seg) {
+        chain->stat.no_buf++;
         return 0;
     }
 
@@ -1730,9 +1734,11 @@ static int virtio_net_rsc_cache_buf(NetRscChain *chain, NetClientState *nc,
     seg->nc = nc;
 
     QTAILQ_INSERT_TAIL(&chain->buffers, seg, next);
+    chain->stat.cache++;
     return size;
 
 out:
+    chain->stat.no_buf++;
     g_free(seg);
     return 0;
 }
@@ -1750,27 +1756,33 @@ static int32_t virtio_net_rsc_handle_ack(NetRscChain *chain, NetRscSeg *seg,
     owin = htons(o_tcp->th_win);
 
     if ((nack - oack) >= TCP_WINDOW) {
+        chain->stat.ack_out_of_win++;
         return RSC_FINAL;
     } else if (nack == oack) {
         /* duplicated ack or window probe */
         if (nwin == owin) {
             /* duplicated ack, add dup ack count due to whql test up to 1 */
+            chain->stat.dup_ack++;
 
             if (seg->dup_ack_count == 0) {
                 seg->dup_ack_count++;
+                chain->stat.dup_ack1++;
                 return RSC_COALESCE;
             } else {
                 /* Spec says should send it directly */
+                chain->stat.dup_ack2++;
                 return RSC_FINAL;
             }
         } else {
             /* Coalesce window update */
             o_tcp->th_win = n_tcp->th_win;
+            chain->stat.win_update++;
             return RSC_COALESCE;
         }
     } else {
         /* pure ack, update ack */
         o_tcp->th_ack = n_tcp->th_ack;
+        chain->stat.pure_ack++;
         return RSC_COALESCE;
     }
 }
@@ -1788,13 +1800,20 @@ static int32_t virtio_net_rsc_coalesce_tcp(NetRscChain *chain, NetRscSeg *seg,
     nseq = htonl(n_tcp->th_seq);
     oseq = htonl(o_tcp->th_seq);
 
+    if (n_tcp_len > sizeof(struct tcp_header)) {
+        /* Log this only for debugging observation */
+        chain->stat.tcp_option++;
+    }
+
     /* Ignore packet with more/larger tcp options */
     if (n_tcp_len > o_tcp_len) {
+        chain->stat.tcp_larger_option++;
         return RSC_FINAL;
     }
 
     /* out of order or retransmitted. */
     if ((nseq - oseq) > TCP_WINDOW) {
+        chain->stat.data_out_of_win++;
         return RSC_FINAL;
     }
 
@@ -1802,16 +1821,19 @@ static int32_t virtio_net_rsc_coalesce_tcp(NetRscChain *chain, NetRscSeg *seg,
     if (nseq == oseq) {
         if ((0 == o_data) && n_data) {
             /* From no payload to payload, normal case, not a dup ack or etc */
+            chain->stat.data_after_pure_ack++;
             goto coalesce;
         } else {
             return virtio_net_rsc_handle_ack(chain, seg, buf, n_tcp, o_tcp);
         }
     } else if ((nseq - oseq) != o_data) {
         /* Not a consistent packet, out of order */
+        chain->stat.data_out_of_order++;
         return RSC_FINAL;
     } else {
 coalesce:
         if ((o_ip_len + n_data) > max_data) {
+            chain->stat.over_size++;
             return RSC_FINAL;
         }
 
@@ -1824,6 +1846,7 @@ coalesce:
 
         memmove(seg->buf + seg->size, data, n_data);
         seg->size += n_data;
+        chain->stat.coalesced++;
         return RSC_COALESCE;
     }
 }
@@ -1855,6 +1878,7 @@ static int32_t virtio_net_rsc_try_coalesce4(NetRscChain *chain,
     if ((n_ip->ip_src ^ o_ip->ip_src) || (n_ip->ip_dst ^ o_ip->ip_dst)
         || (n_tcp->th_sport ^ o_tcp->th_sport)
         || (n_tcp->th_dport ^ o_tcp->th_dport)) {
+        chain->stat.no_match++;
         return RSC_NO_MATCH;
     }
 
@@ -1890,6 +1914,7 @@ static int32_t virtio_net_rsc_try_coalesce6(NetRscChain *chain,
         || memcmp(&n_ip->ip6_dst, &o_ip->ip6_dst, sizeof(struct in6_address))
         || (n_tcp->th_sport ^ o_tcp->th_sport)
         || (n_tcp->th_dport ^ o_tcp->th_dport)) {
+            chain->stat.no_match++;
             return RSC_NO_MATCH;
     }
 
@@ -1927,6 +1952,7 @@ static size_t virtio_net_rsc_callback(NetRscChain *chain, NetClientState *nc,
     NetRscSeg *seg, *nseg;
 
     if (QTAILQ_EMPTY(&chain->buffers)) {
+        chain->stat.empty_cache++;
         if (!virtio_net_rsc_cache_buf(chain, nc, buf, size)) {
             return 0;
         } else {
@@ -1948,6 +1974,7 @@ static size_t virtio_net_rsc_callback(NetRscChain *chain, NetClientState *nc,
             g_free(seg);
             if (ret == 0) {
                 /* Send failed */
+                chain->stat.final_failed++;
                 return 0;
             }
 
@@ -1962,6 +1989,7 @@ static size_t virtio_net_rsc_callback(NetRscChain *chain, NetClientState *nc,
         }
     }
 
+    chain->stat.no_match_cache++;
     return virtio_net_rsc_cache_buf(chain, nc, buf, size);
 }
 
@@ -1980,8 +2008,9 @@ static size_t virtio_net_rsc_drain_one(NetRscChain *chain, NetClientState *nc,
         if ((chain->proto == ETH_P_IP) && seg->is_coalesced) {
             virtio_net_rsc_ipv4_checksum(seg);
         }
-
-        virtio_net_do_receive(seg->nc, seg->buf, seg->size);
+        if (!virtio_net_do_receive(seg->nc, seg->buf, seg->size)) {
+            chain->stat.drain_failed++;
+        }
 
         QTAILQ_REMOVE(&chain->buffers, seg, next);
         g_free(seg->buf);
@@ -2003,20 +2032,24 @@ static int32_t virtio_net_rsc_filter4(NetRscChain *chain, struct ip_header *ip,
 
     /* Not an ipv4 one */
     if (0x4 != ((0xF0 & ip->ip_ver_len) >> 4)) {
+        chain->stat.ip_option++;
         return RSC_BYPASS;
     }
 
     /* Don't handle packets with ip option */
     if (5 != (0xF & ip->ip_ver_len)) {
+        chain->stat.ip_option++;
         return RSC_BYPASS;
     }
 
     /* Don't handle packets with ip fragment */
     if (!(htons(ip->ip_off) & IP_DF)) {
+        chain->stat.ip_frag++;
         return RSC_BYPASS;
     }
 
     if (ip->ip_p != IPPROTO_TCP) {
+        chain->stat.bypass_not_tcp++;
         return RSC_BYPASS;
     }
 
@@ -2024,13 +2057,13 @@ static int32_t virtio_net_rsc_filter4(NetRscChain *chain, struct ip_header *ip,
     ip_len = htons(ip->ip_len);
     if (ip_len < (sizeof(struct ip_header) + sizeof(struct tcp_header))
         || ip_len > (size - IP_OFFSET)) {
+        chain->stat.ip_hacked++;
         return RSC_BYPASS;
     }
 
     return RSC_WANT;
 }
 
-
 static size_t virtio_net_rsc_receive4(void *opq, NetClientState* nc,
                                       const uint8_t *buf, size_t size)
 {
@@ -2048,8 +2081,10 @@ static size_t virtio_net_rsc_receive4(void *opq, NetClientState* nc,
     ret = virtio_net_rsc_parse_tcp_ctrl((uint8_t *)ip,
                                         (0xF & ip->ip_ver_len) << 2);
     if (RSC_BYPASS == ret) {
+        chain->stat.tcp_syn++;
         return virtio_net_do_receive(nc, buf, size);
     } else if (RSC_FINAL == ret) {
+        chain->stat.tcp_ctrl_drain++;
         return virtio_net_rsc_drain_one(chain, nc, buf, size, IP4_ADDR_OFFSET,
                                 IP4_ADDR_SIZE, TCP4_PORT_OFFSET, TCP_PORT_SIZE);
     }
@@ -2073,6 +2108,7 @@ static int32_t virtio_net_rsc_filter6(NetRscChain *chain, struct ip6_header *ip,
 
     /* Both option and protocol is checked in this */
     if (ip->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_TCP) {
+        chain->stat.bypass_not_tcp++;
         return RSC_BYPASS;
     }
 
@@ -2080,6 +2116,7 @@ static int32_t virtio_net_rsc_filter6(NetRscChain *chain, struct ip6_header *ip,
     ip_len = htons(ip->ip6_ctlun.ip6_un1.ip6_un1_plen);
     if (ip_len < sizeof(struct tcp_header)
         || ip_len > (size - TCP6_OFFSET)) {
+        chain->stat.ip_hacked++;
         return RSC_BYPASS;
     }
 
@@ -2101,8 +2138,10 @@ static size_t virtio_net_rsc_receive6(void *opq, NetClientState* nc,
 
     ret = virtio_net_rsc_parse_tcp_ctrl((uint8_t *)ip, sizeof(*ip));
     if (RSC_BYPASS == ret) {
+        chain->stat.tcp_syn++;
         return virtio_net_do_receive(nc, buf, size);
     } else if (RSC_FINAL == ret) {
+        chain->stat.tcp_ctrl_drain++;
         return virtio_net_rsc_drain_one(chain, nc, buf, size, IP6_ADDR_OFFSET,
                         IP6_ADDR_SIZE, TCP6_PORT_OFFSET, TCP_PORT_SIZE);
     }
@@ -2140,6 +2179,7 @@ static NetRscChain *virtio_net_rsc_lookup_chain(NetClientState *nc,
     chain->proto = proto;
     chain->drain_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
                                       virtio_net_rsc_purge, chain);
+    memset(&chain->stat, 0, sizeof(chain->stat));
     if (ETH_P_IP == proto) {
         chain->do_receive = virtio_net_rsc_receive4;
     } else {
@@ -2168,6 +2208,7 @@ static ssize_t virtio_net_rsc_receive(NetClientState *nc,
     if (!chain) {
         return virtio_net_do_receive(nc, buf, size);
     } else {
+        chain->stat.received++;
         return chain->do_receive(chain, nc, buf, size);
     }
 }
@@ -2175,6 +2216,8 @@ static ssize_t virtio_net_rsc_receive(NetClientState *nc,
 static ssize_t virtio_net_receive(NetClientState *nc,
                                   const uint8_t *buf, size_t size)
 {
+    virtio_net_received++;
+
     if (virtio_net_rsc_bypass) {
         return virtio_net_do_receive(nc, buf, size);
     } else {
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index 1383220..fb53291 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -136,6 +136,38 @@ typedef enum {
     RSC_WANT                /* Data want to be coalesced */
 } COALESCE_STATUS;
 
+typedef struct NetRscStat {
+    uint32_t received;
+    uint32_t coalesced;
+    uint32_t over_size;
+    uint32_t cache;
+    uint32_t empty_cache;
+    uint32_t no_match_cache;
+    uint32_t no_buf;
+    uint32_t win_update;
+    uint32_t no_match;
+    uint32_t tcp_syn;
+    uint32_t tcp_ctrl_drain;
+    uint32_t dup_ack;
+    uint32_t dup_ack1;
+    uint32_t dup_ack2;
+    uint32_t pure_ack;
+    uint32_t ack_out_of_win;
+    uint32_t data_out_of_win;
+    uint32_t data_out_of_order;
+    uint32_t data_after_pure_ack;
+    uint32_t bypass_not_tcp;
+    uint32_t tcp_option;
+    uint32_t tcp_larger_option;
+    uint32_t ip_frag;
+    uint32_t ip_hacked;
+    uint32_t ip_option;
+    uint32_t purge_failed;
+    uint32_t drain_failed;
+    uint32_t final_failed;
+    int64_t  timer;
+} NetRscStat;
+
 /* Coalesced segmant */
 typedef struct NetRscSeg {
     QTAILQ_ENTRY(NetRscSeg) next;
@@ -157,6 +189,7 @@ typedef struct NetRscChain {
     VirtioNetReceive *do_receive;
     QEMUTimer *drain_timer;
     QTAILQ_HEAD(, NetRscSeg) buffers;
+    NetRscStat stat;
 } NetRscChain;
 
 void virtio_instance_init_common(Object *proxy_obj, void *data,
