Tracking Down a Memory Leak

Author: Michael Stone

Contents

First, I looked at some tools.

Tools

Splint
A good start, hasn't been maintained in at least 6 months, maybe longer. Outstanding bugs. Also looks like its a lot of effort.
XTC
Perhaps useful for refactoring the entire project, if we knew what we wanted to add.
Cscope
Helpful for figuring out everything that's going on.
Cscope is very helpful for tracking down where a function is used.
You can't do this from within VIM, however. The trick is to run
Cscope from the command line, to perform your search, and then to
hit '>' to save the results.

Next, I decided to track down some kernel memory.

Calls to kmalloc()

As it turns out, there are only 19 calls to kmalloc in the entire program.

File Function Line Code
lib/nswap_hashtable.c <global> 36 htable_t ht = (htable_t)kmalloc(sizeof(htable_t), GFP_NSWAP);
lib/nswap_hashtable.c <global> 40 ht->table=(ht_list_t**)kmalloc(sizeof(ht_list_t*)*num_buckets,
lib/nswap_hashtable.c <global> 49 rsv_ptr=kmalloc(sizeof(ht_list_t), GFP_NSWAP);
lib/nswap_threads.c <global> 81 kmalloc(sizeof(nswap_thread_init_t), GFP_NSWAP);
lib/nswap_hashtable.c get_from_reserves 15 l = (ht_list_t*)kmalloc(sizeof(ht_list_t), GFP_NSWAP);
lib/nswap_hashtable.c ht_iter 198 cur=(ht_list_t*)kmalloc(sizeof(ht_list_t),
lib/nswap_net_io.c nswap_tcp_alloc_socket 24 new_socket = kmalloc(sizeof(struct nswap_socket), GFP_NSWAP);
lib/nswap_net_io.c nswap_udp_alloc_socket 56 new_socket = kmalloc(sizeof(struct nswap_socket), GFP_NSWAP);
lib/nswap_net_io.c nswap_udp_alloc_socket 61 new_socket->addr = kmalloc(sizeof(struct sockaddr), GFP_NSWAP);
src/nswap_iptable.c nswap_iptable_init 185 nswap_iptable = kmalloc(sizeof(struct nswap_ipentry)*NSWAP_MAXHOSTS,
src/nswapc_main.c nswapc_init 292 io_request_sem = kmalloc(sizeof(struct semaphore), GFP_NSWAP);
src/nswapc_main.c nswapc_init 319 worker_args = kmalloc(sizeof(struct worker_thread_args),
src/nswapd_mem.c nswapd_mem_store 91 new_page = kmalloc(sizeof(page_data_t),(GFP_NSWAP));
src/nswapd_mem_mon.c nswapd_mem_monitor_init 91 recent_monitor = kmalloc(sizeof(struct nswap_monitor_data), GFP_NSWAP);
src/nswapd_mem_mon.c nswapd_mem_monitor_init 92 previous_monitor = kmalloc(sizeof(struct nswap_monitor_data), GFP_NSWAP);
src/nswapd_mem_mon_orig.c nswapd_mem_monitor_init 91 recent_monitor = kmalloc(sizeof(struct nswap_monitor_data), GFP_NSWAP);
src/nswapd_mem_mon_orig.c nswapd_mem_monitor_init 92 previous_monitor = kmalloc(sizeof(struct nswap_monitor_data), GFP_NSWAP);
src/nswapd_mem_monitor.c nswapd_mem_monitor_init 93 recent_monitor = kmalloc(sizeof(struct nswap_monitor_data), GFP_NSWAP);
src/nswapd_mem_monitor.c nswapd_mem_monitor_init 94 previous_monitor = kmalloc(sizeof(struct nswap_monitor_data), GFP_NSWAP);

There are 22 calls to kfree.

File Function Line Code
lib/nswap_hashtable.c ht_destroy 62 kfree(ht->table);
lib/nswap_hashtable.c ht_destroy 66 kfree(rsv_ptr);
lib/nswap_hashtable.c ht_destroy 69 kfree(ht);
lib/nswap_net_io.c nswap_tcp_alloc_socket 34 kfree(new_socket);
lib/nswap_net_io.c nswap_udp_alloc_socket 73 kfree(new_socket);
lib/nswap_net_io.c nswap_tcp_close_socket 88 kfree(sock);
lib/nswap_net_io.c nswap_udp_close_socket 99 kfree(sock->addr);
lib/nswap_net_io.c nswap_udp_close_socket 101 kfree(sock);
lib/nswap_threads.c nswap_thread_wrapper 47 kfree(args);
src/nswap_iptable.c nswap_iptable_cleanup 728 kfree(nswap_iptable);
src/nswapc_main.c request_worker_thr 448 kfree(worker_args);
src/nswapc_main.c nswapc_cleanup 1472 kfree(io_request_sem);
src/nswapd_mem.c nswapd_page_unmap_and_unwire 235 kfree(old);
src/nswapd_mem.c nswapd_mem_release 271 kfree(old);
src/nswapd_mem.c nswapd_mem_cleanup 470 kfree(htlist->info);
src/nswapd_mem.c nswapd_mem_cleanup 471 kfree(htlist);
src/nswapd_mem_mon.c nswapd_mem_monitor_cleanup 104 kfree(recent_monitor);
src/nswapd_mem_mon.c nswapd_mem_monitor_cleanup 105 kfree(previous_monitor);
src/nswapd_mem_mon_orig.c nswapd_mem_monitor_cleanup 104 kfree(recent_monitor);
src/nswapd_mem_mon_orig.c nswapd_mem_monitor_cleanup 105 kfree(previous_monitor);
src/nswapd_mem_monitor.c nswapd_mem_monitor_cleanup 106 kfree(recent_monitor);
src/nswapd_mem_monitor.c nswapd_mem_monitor_cleanup 107 kfree(previous_monitor);

Pass 1

ht_iter() only gets called once, in src/nswapd_mem.c:nswapd_mem_cleanup where its results are immediately deleted.

Likewise, nswapd_mem_monitor_init() is only called once in src/nswapd_mem.c:nswapd_mem_init() and its results are freed in the corresponding call nswapd_mem_cleanup()

That leaves us with 12 kmallocs:

File Function Line Code
lib/nswap_hashtable.c <global> 36 htable_t ht = (htable_t)kmalloc(sizeof(htable_t), GFP_NSWAP);
lib/nswap_hashtable.c <global> 40 ht->table=(ht_list_t**)kmalloc(sizeof(ht_list_t*)*num_buckets,
lib/nswap_hashtable.c <global> 49 rsv_ptr=kmalloc(sizeof(ht_list_t), GFP_NSWAP);
lib/nswap_threads.c <global> 81 kmalloc(sizeof(nswap_thread_init_t), GFP_NSWAP);
lib/nswap_hashtable.c get_from_reserves 15 l = (ht_list_t*)kmalloc(sizeof(ht_list_t), GFP_NSWAP);
lib/nswap_net_io.c nswap_tcp_alloc_socket 24 new_socket = kmalloc(sizeof(struct nswap_socket), GFP_NSWAP);
lib/nswap_net_io.c nswap_udp_alloc_socket 56 new_socket = kmalloc(sizeof(struct nswap_socket), GFP_NSWAP);
lib/nswap_net_io.c nswap_udp_alloc_socket 61 new_socket->addr = kmalloc(sizeof(struct sockaddr), GFP_NSWAP);
src/nswap_iptable.c nswap_iptable_init 185 nswap_iptable = kmalloc(sizeof(struct nswap_ipentry)*NSWAP_MAXHOSTS,
src/nswapc_main.c nswapc_init 292 io_request_sem = kmalloc(sizeof(struct semaphore), GFP_NSWAP);
src/nswapc_main.c nswapc_init 319 worker_args = kmalloc(sizeof(struct worker_thread_args),
src/nswapd_mem.c nswapd_mem_store 91 new_page = kmalloc(sizeof(page_data_t),(GFP_NSWAP));

and 14 kfrees.

File Function Line Code
lib/nswap_hashtable.c ht_destroy 62 kfree(ht->table);
lib/nswap_hashtable.c ht_destroy 66 kfree(rsv_ptr);
lib/nswap_hashtable.c ht_destroy 69 kfree(ht);
lib/nswap_net_io.c nswap_tcp_alloc_socket 34 kfree(new_socket);
lib/nswap_net_io.c nswap_udp_alloc_socket 73 kfree(new_socket);
lib/nswap_net_io.c nswap_tcp_close_socket 88 kfree(sock);
lib/nswap_net_io.c nswap_udp_close_socket 99 kfree(sock->addr);
lib/nswap_net_io.c nswap_udp_close_socket 101 kfree(sock);
lib/nswap_threads.c nswap_thread_wrapper 47 kfree(args);
src/nswap_iptable.c nswap_iptable_cleanup 728 kfree(nswap_iptable);
src/nswapc_main.c request_worker_thr 448 kfree(worker_args);
src/nswapc_main.c nswapc_cleanup 1472 kfree(io_request_sem);
src/nswapd_mem.c nswapd_page_unmap_and_unwire 235 kfree(old);
src/nswapd_mem.c nswapd_mem_release 271 kfree(old);

Pass 2

lib/nswap_hashtable.c:get_from_reserves() is only called once in lib/nswap_hashtable.c:ht_insert().

ht_insert() allocates the list element itself. This element is not freed in ht_remove() because ht_remove uses add_to_reserves() to return the newly empty list element to a pool of empty list elements.

The memory is actually freed in ht_destroy().

ht_reset() simply moves all the allocated list elements into the reserved pool.

Now memory that gets sent into the reserved pool will never be freed during the course of the program. However, it does look to me like the hashtable library is clean.

That leaves us with 8 kmallocs:

File Function Line Code
lib/nswap_threads.c nswap_thread 81 kmalloc(sizeof(nswap_thread_init_t), GFP_NSWAP);
lib/nswap_net_io.c nswap_tcp_alloc_socket 24 new_socket = kmalloc(sizeof(struct nswap_socket), GFP_NSWAP);
lib/nswap_net_io.c nswap_udp_alloc_socket 56 new_socket = kmalloc(sizeof(struct nswap_socket), GFP_NSWAP);
lib/nswap_net_io.c nswap_udp_alloc_socket 61 new_socket->addr = kmalloc(sizeof(struct sockaddr), GFP_NSWAP);
src/nswap_iptable.c nswap_iptable_init 185 nswap_iptable = kmalloc(sizeof(struct nswap_ipentry)*NSWAP_MAXHOSTS,
src/nswapc_main.c nswapc_init 292 io_request_sem = kmalloc(sizeof(struct semaphore), GFP_NSWAP);
src/nswapc_main.c nswapc_init 319 worker_args = kmalloc(sizeof(struct worker_thread_args),
src/nswapd_mem.c nswapd_mem_store 91 new_page = kmalloc(sizeof(page_data_t),(GFP_NSWAP));

and 13 kfrees.

File Function Line Code
lib/nswap_net_io.c nswap_tcp_alloc_socket 34 kfree(new_socket);
lib/nswap_net_io.c nswap_udp_alloc_socket 73 kfree(new_socket);
lib/nswap_net_io.c nswap_tcp_close_socket 88 kfree(sock);
lib/nswap_net_io.c nswap_udp_close_socket 99 kfree(sock->addr);
lib/nswap_net_io.c nswap_udp_close_socket 101 kfree(sock);
lib/nswap_threads.c nswap_thread_wrapper 47 kfree(args);
src/nswap_iptable.c nswap_iptable_cleanup 728 kfree(nswap_iptable);
src/nswapc_main.c request_worker_thr 448 kfree(worker_args);
src/nswapc_main.c nswapc_cleanup 1472 kfree(io_request_sem);
src/nswapd_mem.c nswapd_page_unmap_and_unwire 235 kfree(old);
src/nswapd_mem.c nswapd_mem_release 271 kfree(old);

Pass 3