1
0
Fork 0
forked from nuttx/nuttx-update

tools/gdb: Add UDP in netstats

(gdb) netstats
IOB:       size    ntotal     nfree     nwait nthrottle
           1518        72        70         0        38
<...>
UDP Conn:  flg       txbuf       rxbuf         local_address        remote_address
0           41     0/16384     0/16384              :::19279         fc00::1:5001
1           41     0/16384     0/16384              :::19280         fc00::1:5001
2           41     0/16384     0/16384              :::19281         fc00::1:5001
3           41     0/16384     0/16384              :::19282         fc00::1:5001
4           41     0/16384     0/16384              :::19283         fc00::1:5001
5           41     0/16384     0/16384         0.0.0.0:19284        10.0.1.1:5001
6           41     0/16384     0/16384         0.0.0.0:19285        10.0.1.1:5001
7           41     0/16384     0/16384         0.0.0.0:19286        10.0.1.1:5001
8           41     0/16384     0/16384         0.0.0.0:19287        10.0.1.1:5001
9           41     0/16384     0/16384         0.0.0.0:19288        10.0.1.1:5001

Signed-off-by: Zhe Weng <wengzhe@xiaomi.com>
This commit is contained in:
Zhe Weng 2024-08-22 20:18:39 +08:00 committed by Xiang Xiao
parent 1c884ee230
commit 395090284a
2 changed files with 121 additions and 0 deletions

View file

@ -18,8 +18,78 @@
#
############################################################################
import socket
import gdb
import utils
from lists import dq_for_every, sq_for_every
NET_IPv4 = utils.get_symbol_value("CONFIG_NET_IPv4")
NET_IPv6 = utils.get_symbol_value("CONFIG_NET_IPv6")
# NuttX's AF_INET and AF_INET6 have same value as Linux's
AF_INET = socket.AF_INET
AF_INET6 = socket.AF_INET6
def ntohs(val):
"""Convert a 16-bit value from network byte order to host byte order"""
if utils.get_target_endianness() == utils.BIG_ENDIAN:
return val
return utils.swap16(val)
def get_ip_port(conn):
"""Get the IP address and port of a network connection"""
domain = utils.get_field(conn, "domain", AF_INET if NET_IPv4 else AF_INET6)
ip_binding = conn["u"]["ipv4" if domain == AF_INET else "ipv6"]
lport = ntohs(conn["lport"])
rport = ntohs(conn["rport"])
laddr = inet_ntop(domain, ip_binding["laddr"])
raddr = inet_ntop(domain, ip_binding["raddr"])
return laddr, lport, raddr, rport
def inet_ntop(domain, addr):
"""Convert a network address to a string"""
addr_len = 16 if domain == AF_INET6 else 4
return socket.inet_ntop(domain, utils.get_bytes(addr, addr_len))
def socket_for_each_entry(proto):
"""Iterate over a dq of socket structs, usage:
for conn in socket_for_each_entry("icmp"):
readahead = conn["readahead"]
"""
sock_gdbtype = gdb.lookup_type("struct socket_conn_s").pointer()
conn_gdbtype = gdb.lookup_type("struct %s_conn_s" % proto).pointer()
for node in dq_for_every(
gdb.parse_and_eval("g_active_%s_connections" % proto), None
):
yield utils.container_of(
utils.container_of(
node, sock_gdbtype, "node"
), # struct socket_conn_s::dq_entry_t node
conn_gdbtype,
"sconn",
) # udp_conn_s::socket_conn_s sconn
def wrbuffer_inqueue_size(queue=None, protocol="tcp"):
"""Calculate the total size of all iob in the write queue of a udp connection"""
total = 0
if queue:
wrb_gdbtype = gdb.lookup_type("struct %s_wrbuffer_s" % protocol).pointer()
for entry in sq_for_every(queue, None):
entry = utils.container_of(entry, wrb_gdbtype, "wb_node")
total += entry["wb_iob"]["io_pktlen"]
return total
class NetStats(gdb.Command):
@ -83,6 +153,27 @@ class NetStats(gdb.Command):
except gdb.error as e:
gdb.write("Failed to get Net Stats: %s\n" % e)
def udp_stats(self):
try:
gdb.write(
"UDP Conn: %4s %11s %11s %21s %21s\n"
% ("flg", "txbuf", "rxbuf", "local_address", "remote_address")
)
for idx, conn in enumerate(socket_for_each_entry("udp")):
flags = conn["sconn"]["s_flags"]
txbuf = utils.get_field(conn, "sndbufs", -1)
rxbuf = utils.get_field(conn, "rcvbufs", -1)
txsz = wrbuffer_inqueue_size(utils.get_field(conn, "write_q"), "udp")
rxsz = conn["readahead"]["io_pktlen"] if conn["readahead"] else 0
laddr, lport, raddr, rport = get_ip_port(conn)
gdb.write(
"%-4d %4x %5d/%-5d %5d/%-5d %15s:%-5d %15s:%-5d\n"
% (idx, flags, txsz, txbuf, rxsz, rxbuf, laddr, lport, raddr, rport)
)
except gdb.error as e:
gdb.write("Failed to get UDP stats: %s\n" % e)
def invoke(self, args, from_tty):
if utils.get_symbol_value("CONFIG_MM_IOB"):
self.iob_stats()
@ -90,6 +181,9 @@ class NetStats(gdb.Command):
if utils.get_symbol_value("CONFIG_NET_STATISTICS"):
self.pkt_stats()
gdb.write("\n")
if utils.get_symbol_value("CONFIG_NET_UDP"):
self.udp_stats()
gdb.write("\n")
if utils.get_symbol_value("CONFIG_NET"):

View file

@ -177,6 +177,16 @@ def get_field(val, key, default=None):
return default
def get_bytes(val, size):
"""Convert a gdb value to a bytes object"""
try:
return val.bytes[:size]
except AttributeError: # Sometimes we don't have gdb.Value.bytes
inf = gdb.inferiors()[0]
mem = inf.read_memory(val.address, size)
return mem.tobytes()
def import_check(module, name="", errmsg=""):
try:
module = __import__(module, fromlist=[name])
@ -345,6 +355,23 @@ def read_ulong(buffer, offset):
return read_u32(buffer, offset)
def bswap(val, size):
"""Reverses the byte order in a gdb.Value or int value of size bytes"""
return int.from_bytes(int(val).to_bytes(size, byteorder="little"), byteorder="big")
def swap16(val):
return bswap(val, 2)
def swap32(val):
return bswap(val, 4)
def swap64(val):
return bswap(val, 8)
target_arch = None