diff --git a/doc/nethogs.8 b/doc/nethogs.8 index efa5f1e..7c987b7 100644 --- a/doc/nethogs.8 +++ b/doc/nethogs.8 @@ -19,6 +19,7 @@ nethogs \- Net top tool grouping bandwidth per process .RB [ "\-l" ] .RB [ "\-f filter" ] .RB [ "\-C" ] +.RB [ "\-g period" ] .RI [device(s)] .SH DESCRIPTION NetHogs is a small 'net top' tool. Instead of breaking the traffic down per protocol or per subnet, like most such tools do, it groups bandwidth by process - and does not rely on a special kernel module to be loaded. So if there's suddenly a lot of network traffic, you can fire up NetHogs and immediately see which PID is causing this, and if it's some kind of spinning process, kill it. @@ -61,6 +62,9 @@ monitor all devices, even loopback/stopped ones. \fB-C\fP capture TCP and UDP. .TP +\fB-g\fP +garbage collection period in number of refresh. default is 50. +.TP \fB-f\fP EXPERIMENTAL: specify string pcap filter (like tcpdump). This may be removed or changed in a future version. .TP diff --git a/src/inode2prog.cpp b/src/inode2prog.cpp index dec0568..8ec1f65 100644 --- a/src/inode2prog.cpp +++ b/src/inode2prog.cpp @@ -20,6 +20,7 @@ * */ +#include #include #include #include @@ -30,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -224,6 +226,73 @@ void get_info_for_pid(const char *pid) { closedir(dir); } +static quad_t get_ms() { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return static_cast(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000; +} + +static void get_pids(std::set *pids) { + DIR *proc = opendir("/proc"); + if (proc == 0) { + std::cerr << "Error reading /proc, needed to get pid set" << std::endl; + exit(1); + } + dirent *entry; + + while ((entry = readdir(proc))) { + if (entry->d_type != DT_DIR) + continue; + + if (!is_number(entry->d_name)) + continue; + + pids->insert(str2int(entry->d_name)); + } + closedir(proc); +} + +void garbage_collect_inodeproc() { + static quad_t last_ms = 0; + quad_t start_ms = 0; + if (bughuntmode) { + start_ms = get_ms(); + if (last_ms) { + std::cout << "PERF: GC interval: " << start_ms - last_ms << "[ms]" + << std::endl; + } + } + + std::set pids; + get_pids(&pids); + if (pids.size() == 0) { + return; + } + + for (std::map::iterator it = inodeproc.begin(); + it != inodeproc.end();) { + if (!it->second || pids.find(it->second->pid) != pids.end()) { + ++it; + continue; + } + + if (bughuntmode) { + std::cout << "GC prg_node (inode=" << it->first + << ", pid=" << it->second->pid + << ", cmdline=" << it->second->cmdline.c_str() << ")" + << std::endl; + } + delete it->second; + inodeproc.erase(it++); + } + + if (bughuntmode) { + last_ms = get_ms(); + std::cout << "PERF: GC proctime: " << last_ms - start_ms << "[ms]" + << std::endl; + } +} + /* updates the `inodeproc' inode-to-prg_node mapping * for all processes in /proc */ void reread_mapping() { diff --git a/src/inode2prog.h b/src/inode2prog.h index c9b0244..d79fd5f 100644 --- a/src/inode2prog.h +++ b/src/inode2prog.h @@ -41,4 +41,6 @@ void prg_cache_clear(); // reread the inode-to-prg_node-mapping void reread_mapping(); +void garbage_collect_inodeproc(); + #endif diff --git a/src/main.cpp b/src/main.cpp index f81819e..52964ce 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,6 +46,8 @@ static void help(bool iserror) { output << " -a : monitor all devices, even loopback/stopped " "ones.\n"; output << " -C : capture TCP and UDP.\n"; + output << " -g : garbage collection period in number of refresh. " + "default is 50.\n"; output << " -f : EXPERIMENTAL: specify string pcap filter (like " "tcpdump)." " This may be removed or changed in a future version.\n"; @@ -140,9 +142,10 @@ int main(int argc, char **argv) { int promisc = 0; bool all = false; char *filter = NULL; + int garbage_collection_period = 50; int opt; - while ((opt = getopt(argc, argv, "Vhbtpsd:v:c:laf:C")) != -1) { + while ((opt = getopt(argc, argv, "Vhbtpsd:v:c:laf:Cg:")) != -1) { switch (opt) { case 'V': versiondisplay(); @@ -184,6 +187,9 @@ int main(int argc, char **argv) { case 'C': catchall = true; break; + case 'g': + garbage_collection_period = (time_t)atoi(optarg); + break; default: help(true); exit(EXIT_FAILURE); @@ -294,6 +300,7 @@ int main(int argc, char **argv) { struct dpargs *userdata = (dpargs *)malloc(sizeof(struct dpargs)); // Main loop: + int refresh_count = 0; while (1) { bool packets_read = false; @@ -322,6 +329,12 @@ int main(int argc, char **argv) { ui_tick(); } do_refresh(); + ++refresh_count; + + if ((garbage_collection_period > 0) && + (refresh_count % garbage_collection_period == 0)) { + garbage_collect_processes(); + } } // if not packets, do a select() until next packet diff --git a/src/process.cpp b/src/process.cpp index 6e2b324..0c8f03e 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -424,3 +424,5 @@ void remove_timed_out_processes() { previousproc = curproc; } } + +void garbage_collect_processes() { garbage_collect_inodeproc(); } diff --git a/src/process.h b/src/process.h index 8aa7710..577146e 100644 --- a/src/process.h +++ b/src/process.h @@ -145,4 +145,6 @@ void procclean(); void remove_timed_out_processes(); +void garbage_collect_processes(); + #endif