2 # dmon -- DTrace-backed TCP/IP real-time monitoring utility
3 # Copyright (C) 2022 Sergey Matveev <stargrave@stargrave.org>
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, version 3 of the License.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ########################################################################
23 die "Usage: $0 [tcp|ifname0 ifname1 ...]\n";
25 use File::Temp qw/tempfile/;
26 my ($dfh, $dfn) = tempfile();
29 #pragma D option quiet
30 #pragma D option switchrate=10Hz
36 if ($ARGV[0] eq "tcp") {
38 my ($probe, $dir) = @_;
42 @" . $dir . "[args[2]->ip_saddr,
45 args[4]->tcp_dport] = sum(args[2]->ip_plength);
49 tcpprobe "tcp:::send", "tx";
50 tcpprobe "tcp:::receive", "rx";
54 printa("< [%s]:%d [%s]:%d %@d\n", @rx);
56 printa("> [%s]:%d [%s]:%d %@d\n", @tx);
58 printf("I %d\n", rxpkts);
59 printf("O %d\n", txpkts);
66 my ($probe, $dir) = @_;
67 my $conds = join " || ", map { "args[3]->if_name == \"$_\"" } @ARGV;
72 @" . $dir . "[args[2]->ip_saddr,
73 args[2]->ip_daddr] = sum(args[2]->ip_plength);
77 ipprobe "ip:::send", "tx";
78 ipprobe "ip:::receive", "rx";
82 printa("< %s %s %@d\n", @rx);
84 printa("> %s %s %@d\n", @tx);
86 printf("I %d\n", rxpkts);
87 printf("O %d\n", txpkts);
94 my $height = (defined $ENV{DMON_HEIGHT}) ? $ENV{DMON_HEIGHT} : 40;
97 ########################################################################
99 my @sizePrefix = ("E", "P", "T", "G", "M", "K");
100 my @countBound = (1e18, 1e15, 1e12, 1e9, 1e6, 1e3);
101 my @sizeBound = (1<<60, 1<<50, 1<<40, 1<<30, 1<<20, 1<<10);
104 my ($s, $suffix, $bounds) = @_;
105 foreach my $i (0 .. $#{$bounds}) {
106 next if $s < @{$bounds}[$i];
107 return sprintf "%.2f %s%s", $s / @{$bounds}[$i], $sizePrefix[$i], $suffix;
112 sub humanCount { return human shift, "", \@countBound }
114 sub humanSize { return human shift, "iB", \@sizeBound }
116 ########################################################################
119 my ($pktsRx, $pktsTx, $bytesRx, $bytesTx) = (0, 0, 0, 0);
121 @>>>>>>>>>> / @<<<<<<<<<< @>>>>>>>>>>>> / @<<<<<<<<<<<<
122 humanCount($pktsRx), humanCount($pktsTx), humanSize($bytesRx), humanSize($bytesTx)
123 ------------------------------------------------------------------------
126 my ($left, $dir, $right, $size);
128 @<<<<<<<<<<<<<<<<<<<<<<<<<< @| @>>>>>>>>>>>>>>>>>>>>>>>>>> @>>>>>>>>>>>>
129 $left, $dir, $right, humanSize($size)
132 ########################################################################
135 open $dexec, "-|", "dtrace -s $dfn" or die "$!";
141 next unless /^[<>TIO]/;
143 if ($cols[0] eq "T") {
145 foreach my $src (keys %rx) {
146 foreach my $dst (keys %{$rx{$src}}) {
147 push @res, [$dst, "<", $src, $rx{$src}{$dst}];
150 foreach my $src (keys %tx) {
151 foreach my $dst (keys %{$tx{$src}}) {
152 push @res, [$src, ">", $dst, $tx{$src}{$dst}];
155 @res = sort { @{$b}[-1] <=> @{$a}[-1] } @res;
156 printf "\033[H\033[J";
158 foreach (@res[0..$height]) {
160 ($left, $dir, $right, $size) = @{$_};
164 ($pktsRx, $pktsTx, $bytesRx, $bytesTx) = (0, 0, 0, 0);
167 if ($cols[0] eq "I") {
171 if ($cols[0] eq "O") {
175 if ($cols[0] eq "<") {
178 $bytesRx += $cols[3];
182 $bytesTx += $cols[3];
184 if (defined ${$xx}{$cols[1]}) {
185 ${$xx}{$cols[1]}{$cols[2]} += $cols[3];
187 ${$xx}{$cols[1]} = {$cols[2] => $cols[3]};