debugging application
- debug application
- show dynamic library used by an application
- create and use a static library
- create and use a dynamic library
- show symbols in dynamics and static library
- check process thread
- do everything in memory
- debug dynamic library info when runs a program
- useful commands in binutils
- process affinity
- IRQ affinity setting
- trace process by strace
- show shared memory on the system
- only show mem/cpu usage of a particular pid
- show processes who opened a file or dir
- force a running process to generate a core dump
- signal
- show environment variables of a running process.
- check process name when knows pid
- memory leak/ overflow tool
- shutdown() vs close()
- RESET TCP from application
- install debuginfo of kernel or application on centos
- show stack for give process(thread)
- pkg-config
- ld finding library
- get hardware meta
- lspci output
- coredump without symbol table
- network related
- enable forwarding in kernel
- IP address and Route
- Deep into route table
- prevent route missing after reboot
- show arp entry
- save tcpdump captured packet to file
- packet shows as tcp segment of a reassembled pdu in wireshark
- get two sides of established STREAM socket
- show all sockets
- check/set mtu or MAC
- socket bind to no-local address
- source port selection
- create vlan interface and check its real dev
- check tcp fastopen cookie
- How to know if a network interface is tap, tun, bridge or physical
- bind irq(s) of ethx to specific cpu
- check rss of ethx
- get bus info for a network device like eth0
- create binding interface through sysfs
- icmp ping is ok but tcp connect fails
debug application
Here are list of frequent commands that are used in daily life and some tips that may be useful to debug application
tested only on centos7 platform
show dynamic library used by an application
$ ldd app_binary
output:
linux-vdso.so.1 => (0x00007fff76bcd000)
libopenvswitch.so.1 => /usr/lib/libopenvswitch.so.1 (0x00007f19c83c2000)
libboost_system.so.1.54.0 => /usr/lib/x86_64-linux-gnu/libboost_system.so.1.54.0 (0x00007f19c81be000)
…
$ readelf -d app_binary | grep NEEDED
output:
0x0000000000000001 (NEEDED) Shared library: [libopenvswitch.so.1]
0x0000000000000001 (NEEDED) Shared library: [libboost_system.so.1.54.0]
…
then find the library path
$ locate libopenvswitch.so.1
/usr/lib/debug/usr/lib/libopenvswitch.so.1.0.0
/usr/lib/libopenvswitch.so.1
/usr/lib/libopenvswitch.so.1.0.0
how application searches library
- directories listed in the LD_LIBRARY_PATH environment variable (DYLD_LIBRARY_PATH on OSX)
- directories listed in the executable’s rpath($chrpath -l app)
- directories on the system search path, which (on Linux at least) consists of the entries in /etc/ld.so.conf plus /lib and /usr/lib.
create and use a static library
create static lib from object files
$ ar cru libopenvswitch.a stream-ssl.o stream.o
later on you may want to add a new object in the lib
$ ar crs libopenvswitch.a foo.o
use it in your application
$ gcc -o app main.c /xx/libopenvswitch.a
OR
$ gcc -o app main.c -lopenvswitch -L/path/to/openvswitch
when adding library in gcc command line the order is important, let's say if A.a depends on B.a, the command should be $ gcc -o app main.c A.a B.a
show all objects in static library
$ ar -t libopenvswitch.a
extract object file from static library
$ ar -xv libopenvswitch.a stream-ssl.o
create and use a dynamic library
$ gcc -fPIC -g foo.c
$ gcc -shared -o libfoo.so foo.o
Use it later on by this way
$ gcc -o app main.c libfoo.so
Or
$ gcc -app main.c -lfoo -L/path/to/foo
show symbols in dynamics and static library
$ readelf -s /usr/lib/x86_64-linux-gnu/libssl.so
$ readelf -Ws /usr/lib/x86_64-linux-gnu/libssl.a
$ readelf -Ws stream-ssl.o
-W==–wide means show full name of the long symbol
check process thread
show all processes(not thread) of a given program
$ pidof program_name
-s Single shot - this instructs the program to return only one pid
$ pidof -s program_name
-x Scripts too - this causes the program to also return process id’s of shells running the named scripts
$ pidof -x shell_scripts
-o omit pid Tells pidof to omit processes with that process id.
The special pid %PPID can be used to name the parent process of the pidof program, in other words the calling shell or shell script.
example:
if pidof -o %PPID -x “abc.sh”>/dev/null; then
echo “Process already running”
fi
show threads in a process
$ ps -Lf $pid
Or see from /proc
$ ls /proc/$pid/task
do everything in memory
As in some case, you have large memory, you can run all commands in memory, it's fast, but also note these files
will be lost after reboot.
For example compile kernel in memory
$ tar xjvf linux.tar.gz -C /dev/shm
$ cd /dev/shm/linux
$ cp /boot/config-4.3.3-4.3.y.20151215.ol7.x86_64 .config
$ make oldconfig && make -j32
$ make -j32 modules_install
$ make install
debug dynamic library info when runs a program
1 | $ LD_DEBUG=help ls |
useful commands in binutils
addr2line, ar, gprof, nm, objdump, readelf, gcov, strip, ranlib, size, strings
ranlib: generates an index to the contents of an archive
(below will generate symbol index for the archive but actually, it's not need as 'ar' already takes care of it, you can use 'nm -s xxx.a' to check the symbol before running ranlib)
$ ranlib xxx.a
size: list the section size of an object or archive file
strings: list printable strings from files
(note strings only return printable characters, strings displays all strings that are at least four characters in length in the files but can change with -n)
strings is always used to get string from Binary file!!!
$ strings /bin/ls | grep Copyright
show c++ symbol with namespace
$ nm –demangle xxx.a
(–demangle shows symbol like namespace::Builder::hello(int); that is readable for human not ABXxxhello33x which is really stored by compiler)
output format for nm
A: Archive symbol (from an archive file)
B: BSS segment (uninitialized data)
C: Common symbol (a type of BSS)
D: Data segment (initialized data)
G: Global symbol (for dynamic symbols, typically in shared objects)
I: Indirect symbol (used in dynamic linking)
N: Debugging section (symbol with no defined value)
R: Read-only data segment
S: Stack segment
T: Text segment (code)
U: Undefined symbol (a symbol that is referenced but not defined in this object file)
W: Weak symbol (a symbol that can be overridden by a definition with the same name)
t: Local (static) text (code) symbol
d: Local (static) data symbol
process affinity
1 | task state: ZOMBIE(if child exits, it enters this STATE, waiting parent to read it state, after parent read it by wait() etc, child resource(task_struct) is freed) |
IRQ affinity setting
see cpu affinity for IRQ
$ cat /proc/irq/145/smp_affinity
f
(0x1111)
$ cat /proc/irq/145/smp_affinity_list
0-3
(cpu0, cpu1, cpu2, cpu3)
trace process by strace
strace/ltrace/ptrace/truss
show how much time process spent on system call or library, (the library is C library like, memset, fgets etc)
ptrace is a system call
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
strace and ltrace are based on ptrace to implement their feature
strace/truss is used to trace system call and signal when it occurs
ltrace is used to trace a process's library call initially
truss is similar to strace, but it's for UNIX, not linux
strace is tool we selected for linux
#mount -t procfs proc /proc
#strace -f -tt -o vim.strace vim
/*attach a process to see what system (lib) is calling */
#strace -p pid
#strace -f -p pid
(monitor parent and all its children, this is what -f option does)
#strace -o filename
(save result to a file)
#strace -T
(show time spent on system call)
#strace -t
(show time of day)
#strace -s 1024
(max sie of string to print)
#strace -e trace=nanosleep
-e trace=network
-e trace=file
-e trace=desc
-e trace=signal
(trace a particular event)
7) strace -f
(trace child as well)
8) strace -c
(Count time, calls, and errors for each system call and report a summary on program exit)
ltrace also has options above!!!!!
so the usually use case is
$ strace -f -c -o result vim
show time, spending time for system call
$ strace -f -t -T -o result vim
root@manager:~# strace -f -c vim
1 | % time seconds usecs/call calls errors syscall |
show shared memory on the system
show ipc shared memory
$ ipcs
show posix shared memory
$ ls /dev/shm
only show mem/cpu usage of a particular pid
$ ps -p 13687 -o %mem=
$ ps -p 13687 -o %cpu=
show processes who opened a file or dir
who open the dir only(top level)
$ lsof $dir
search for all open instances of directory s and the files and directories it contains at its top level
$ lsof +d $s
search for all open instances of directory D and all the files and directories it contains to its complete depth
$ lsof +D $s
only this file, who opens the file
$ lsof $file
force a running process to generate a core dump
gcore(from gdb) does NOT kill the process
$ gcore $pid
(usually to see the snapshot of the process memory)
kill the process as send abort to the process
$ kill -SIGABRT $pid
signal
A few days ago, i landed upon unix signals that lead to process termination. I guess i was trying to remember the signals generated in linux when one presses Ctrl+Z and Ctrl+C. Memory did not serve me at that moment and i decided to look these up, one more time. I realized that having a consolidated book which explains these terms clearly is better than searching loads of webpages. I did the later since i had kept my unix os book away from my reach.To my disappointment, there was no single link that listed out all differences in an orderly fashion.
Hence, in this post, i wish to delineate these terms by consolidating my findings from stackoverflow, wikipedia and other unix internals websites. Here it goes:
SIGKILL: Terminates a process immediately. This signal cannot be handled (caught), ignored or blocked. (The “kill -9” command in linux generates the same signal).
SIGTERM: Terminates a process immediately. However, this signal can be handled, ignored or caught in code. If the signal is not caught by a process, the process is killed. Also, this is used for graceful termination of a process. (The “kill” command in linux if specified without any signal number like -9, will send SIGTERM)
SIGINT: Interrupts a process. (The default action is to terminate gracefully). This too, like, SIGTERM can be handled, ignored or caught. The difference between SIGINT and SIGTERM is that the former can be sent from a terminal as input characters. This is the signal generated when a user presses Ctrl+C. (Sidenote: Ctrl+C denotes EOT(End of Transmission) for (say) a network stream)
SIGQUIT: Terminates a process. This is different from both SIGKILL and SIGTERM in the sense that it generates a core dump of the process and also cleans up resources held up by a process. Like SIGINT, this can also be sent from the terminal as input characters. It can be handled, ignored or caught in code. This is the signal generated when a user presses Ctrl+.
SIGSTP: Suspends a process. This too, can be handled, ignored or blocked. Since it does not terminate the process, the process can be resumed by sending a SIGCONT signal. This signal can be generated by pressing Ctrl+Z. (Sidenote: Ctrl+Z stands for substitute character which indicates End-of-File in DOS)
SIGHUP: (From Wikipedia): Hangs up a process when the controlling terminal is disconnected. This especially relates to modem/dial in connections. A process has to explicitly handle this signal for it to work. A good use is to “poke” a process and letting the process (as defined by the programmer) decide what to do with the signal is described here. Hence, SIGHUP can be handled, ignored or caught. This is the signal generated when a user presses Ctrl+D.
Some time, you may want to hang a process for low response
$ kill -STOP $pid
$ kill -SIGCONT $pid
show environment variables of a running process.
use gdb, environ is a global variable
environment variables of this process, it’s the final place, absolute right!
attach to that process
gdb> p environ
gdb>environ[0]
gdb>environ[1]
gdb>environ[2]
…
gdb>environ[x]
check /proc/PIDxx/environ
it only shows the environment variables when process launches, if you added
new environment variable by setenv()[C function], it doesn’t have one that
added after process launched!!!
check process name when knows pid
$ cat /proc/$pid/status
memory leak/ overflow tool
Use tools:
valgrind and AddressSanitizer
shutdown() vs close()
1 | shutdown() is useful for deliniating when you are done providing a request to a server using TCP. A typical use is to send a request to a server followed by a shutdown(). The server will read your request followed by an EOF (read of 0 on most unix implementations). This tells the server that it has your full request. You then go read blocked on the socket. The server will process your request and send the necessary data back to you followed by a close. When you have finished reading all of the response to your request you will read an EOF thus signifying that you have the whole response. |
RESET TCP from application
if application sent RST(RST flag set) on a tcp socket, the socket will go into Closed state imediately(no FIN sent)
, the peer that receives RST packet will go into peer Closed state as well
, no need ACK for RST packet
, this is the quick way to close tcp conenction and free port.
1 | struct linger linger; |
install debuginfo of kernel or application on centos
1 | # debuginfo of kernel(kernel is not built by yourself) |
show stack for give process(thread)
1 | # show task of the group |
pkg-config
pkg-config
is a tool to check dependencies for a library, it outputs version, header path, libs
of that library, so that someone who uses this library passes these to compiler for building.
pkg-config
gets all these information by checking xxx.pc
from several paths, so that if a library wants to be managed by pkg-config
, it must proivde a xxx.pc file at some path.
/usr/lib64/pkgconfig/glib.pc
1 | prefix=/usr |
1 | # get default search paths for pkg-config |
ld finding library
1 | # when for linking, ld is used, show paths checked for a library |
get hardware meta
1 | $dmidecode |
lspci output
lspci
only shows pci(e) devices which means in-use pci(e) slot, more detail about pci device by lspci and setpci to query and set pci registers
1 | # list all pci devices |
coredump without symbol table
As in production env, symbol table is stripped to reduce the binary size, but external symbol table is generated and stored somewhere usually, so that when core happens, it’s esay for us to know why.
but what about no external symbol what we can do?
Stack analysis: Analyze the stack trace in the core dump to understand the sequence of function calls leading up to the crash(use virtual address and objdump -d binary to find the assembly code, with the assembly code, try to map the source). This can provide insights into the program’s execution flow and potentially identify the source of the problem.
Register analysis: Examine the register values in the core dump to understand the state of the program at the time of the crash. This can help you identify any abnormal values or conditions that may have caused the crash.
Keep in mind that without symbols, the information obtained from the core dump analysis will be limited. It will be difficult to pinpoint the exact cause of the crash or understand the specific code paths leading to it.
network related
enable forwarding in kernel
open /etc/sysctl.conf
enable net.ipv4.ip_forward=1
enable ipv6 forwarding set net.ipv6.conf.all.forwarding=1
then restart sysctl service
Actually, all variables in /etc/sysctl.conf will be applied to corresponding kernel variables through /proc or sysctl API
Note: proc for sysctl is at /proc/sys
$ cat /proc/sys/net/ipv4/ip_forward
IP address and Route
IP Address
Displaying existing addresses
ip [-6] addr show [dev
Add an IP address
ip [-6] addr add
ip -6 addr add 2001:0db8:0:f101::1/64 dev eth0
Removing an IP address
ip [-6] addr del
ip -6 addr del 2001:0db8:0:f101::1/64 dev eth0
IP Route
show ip routes on all interface
ip [-6] route show
show ip routes on particular interface
ip [-6] route show dev eth0
Add an IP route through a gateway
ip [-6] route add
ip -6 route add 2000::/64 via 2001:0db8:0:f101::1
Removing an IP route through a gateway
ip [-6] route del
ip -6 route del 2000::/3 via 2001:0db8:0:f101::1
Add an IP route through an interface
ip [-6] route add
ip -6 route add 2000::/3 dev eth0 metric 1
Removing an IP route through an interface
ip [-6] route del
ip -6 route del 2000::/3 dev eth0
Deep into route table
Actually, kernel supports 255 route tables with priority, the two main
used ones are local table and main table, local table can’t be modified, controlled by kernel
while main table is the default table when you use tools to add/delete/create route, they are in main table
which table should we use when packet comes in?
First check local table, if not match, check main table, DO NOT check other table if no
policy(ip rule) is configure, otherwise, check other table based on rule, skip local and main
For example, tag skb with mark 1, if skb with mark 1, lookup table 100
iptables -t mangle -A PREROUTING -j MARK –set-mark 1
ip rule add fwmark 1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
Show routes in different tables(default is main)
ip -6 route show
ip -6 route show table main
ip -6 route show table all
ip -6 route show table local
ip -6 route show table 100
lets explain the output fields for each route with ipv4 as example
1 | $ ifconfig |
prevent route missing after reboot
There are two ways to do this
add static route
1
2
3
4
5
6
7# Centos
# /etc/sysconfig/network-scripts/route-eth0 created if not there
default via 10.10.10.1 dev eth0
10.117.0.0/16 via 10.117.1.1 dev eth0
# from terminal restart network service
$sudo service network restartsave route table and restore it after reboot
1
2
3
4$sudo ip route save >dump
# after reboot
$sudo ip route restore <dump
show arp entry
1 | # incomplete means request is sent and no reply |
save tcpdump captured packet to file
$ tcpdump -i eth0 -s 0 -w /home/lzq/pt.pcap
-s limits the size of packet
-o indicates no limition!
packet shows as tcp segment of a reassembled pdu in wireshark
This is due to application message(PDU) is larger than MSS, tcp splits it into several tcp segments, but tcp segment has no Fragment flag, how does wireshark know several tcp segments belong to same PDU?
wireshark thinks if several tcp segments have the same ACK number but different sequence numbers, they belong to same PDU, refer to
get two sides of established STREAM socket
STREAM socket can be over TCP or unix, for TCP, it’s easy to get it, here we foucus on UNIX STREAM socket with ss
command.
1 | State Recv-Q Send-Q Local Address:Port Peer Address:Port local process who uses local address |
show all sockets
$ netstat -a -t #all tcp sockets
$ netstat -a -u #all udp sockets
$ netstat -a -w #all raw sockets
$ netstat -a -x #all unix sockets
$ netstat -tulpn #l means listening p with program name
Note: if -a is not present, it will only display socket in established state
Show all files opened by a process and -P(show number not name for host, port)
$ lsof -p pid -P
show which process opens a socket on particular port
# lsof -i:80
check/set mtu or MAC
$ ifconfig eth1
$ ifcofnig eth1 mtu 1500
temporary
$ ifconfig eth0 down
$ ifconfig eth0 hw ether 00:80:48:BA:d1:30
$ ifconfig eth0 up
permanently
Centos7
edit /etc/sysconfig/network-scripts/ifcfg-eth0
MACADDR=02:01:02:03:04:0
socket bind to no-local address
if you bind() normally, the ip address must be one of the host’s address
bind can be usedin two cases:
- server call socket/bind/listen/accpet, that means only the dst is for bind address, it will accept it, otherwise no.
- client call bind, socket/bind/send, that means client uses the bind address as the source address when sending packet.
but for some case, you want to bind an address which is no-local(none of the host address) which is always need for loadbalancer server!
global setting, so that every application can bind non local address
$ echo 1 > /proc/sys/net/ipv4/ip_nonlocal_bind
enable it on a socket of the process
In programe code, call setsocketopt(IP_TRANSPARENT, 1), then
$ setcap CAP_NET_ADMIN program(before program runs)
IP_TRANSPARENT (since Linux 2.6.24)
Setting this boolean option enables transparent proxying on
this socket. This socket option allows the calling
application to bind to a nonlocal IP address and operate both
as a client and a server with the foreign address as the local
endpoint.
NOTE: for receiving non-local address that requires routing should be set up in a way that packets going to the foreign address are routed through the TProxy box (i.e., the system hosting the application that employs the IP_TRANSPARENT socket option), make sure packet is not dropped before reaching tproxy module in kernel
Enabling this socket option requires superuser privilege(the CAP_NET_ADMIN capability).
source port selection
if not set source port explicitly by bind(), source port selected from below range
$ cat /proc/sys/net/ipv4/ip_local_port_range
32768 60999
change the source port range
$ sysctl -w net.ipv4.ip_local_port_range=1024 4095
create vlan interface and check its real dev
As vlan interface is a virtual device, so it must attach to a ‘real’ device, ‘real’ does not mean it must be a physical, it could be another virtual device as well, when send traffic on the vlan interface, it sends out to ‘real’ device after adding vlan id, call ‘real’ device’s dev_queue_xmit().
1 | # must provide 'real' device when creating vlan interface, two vlan interfaces can point to same 'real' deivce. |
check tcp fastopen cookie
When tcp fastopen is enabled both on client and server side, client sends cookie request as TCP option, server sends cookie as TCP option as well, client saves it at kernel with key(dstip: port), when next TCP SYN will send the cookie if for same dst:port
1 | # check cookie saved by kernel |
NOTE when TCP fastopen is enabled(setsockopt), use send() not connect() to setup TCP connection
How to know if a network interface is tap, tun, bridge or physical
ethtool -i tunOrTapDeviceName
- In case of a TAP device we will get: “bus-info: tap”.
- In case of a TUN device we will get: “bus-info: tun”.
1 | $ ethtool -i vnet0 |
bind irq(s) of ethx to specific cpu
do it by yourself
- get irq of the ethx
- set affinity of each irq
use vendor script
- Mellanox: /usr/sbin/set_irq_affinity_bynode.sh socket ethN
- Chelsio: /sbin/t4_perftune.sh
1 | # get irq of eth0, for ethx that support multiple queues, for each queue there is a irq number |
check rss of ethx
1 | #+++++++++++++++++++++++++++++ One Way++++++++++++++++++++++++++++++++++++++++ |
get bus info for a network device like eth0
1 | ################# use ethtool===================== |
create binding interface through sysfs
1 | #first check all existing bindings |
icmp ping is ok but tcp connect fails
Tcp connection fails with ‘NO route to host’ while ping is ok, it’s probably packet is dropped by firewall.
1 | # --- Way1: use tcpdump to see where packet dropped |