Initial commit
authorSergey Matveev <stargrave@stargrave.org>
Tue, 6 Sep 2022 19:10:13 +0000 (22:10 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Tue, 6 Sep 2022 19:44:08 +0000 (22:44 +0300)
dmon.pl [new file with mode: 0755]

diff --git a/dmon.pl b/dmon.pl
new file mode 100755 (executable)
index 0000000..885d9b2
--- /dev/null
+++ b/dmon.pl
@@ -0,0 +1,157 @@
+#!/usr/bin/env perl
+# dmon -- DTrace-backed TCP/IP real-time monitoring utility
+# Copyright (C) 2022 Sergey Matveev <stargrave@stargrave.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+########################################################################
+
+if ($#ARGV < 0) {
+    die "Usage: $0 [tcp|ifname0 ifname1 ...]\n";
+};
+use File::Temp qw/tempfile/;
+my ($dfh, $dfn) = tempfile();
+END { unlink $dfn };
+print $dfh "
+#pragma D option quiet
+#pragma D option switchrate=10Hz
+
+profile:::tick-1sec
+{
+    printf(\"T\\n\");
+}\n";
+if ($ARGV[0] eq "tcp") {
+    sub tcpprobe {
+        my ($probe, $dir) = @_;
+        print $dfh "
+$probe
+{
+    printf(\"$dir [%s]:%d [%s]:%d %d\\n\",
+        args[2]->ip_saddr,
+        args[4]->tcp_sport,
+        args[2]->ip_daddr,
+        args[4]->tcp_dport,
+        args[2]->ip_plength);
+}\n";
+    };
+    tcpprobe "tcp:::send", ">";
+    tcpprobe "tcp:::receive", "<";
+} else {
+    sub ipprobe {
+        my ($probe, $dir) = @_;
+        my $conds = join " || ", map { "args[3]->if_name == \"$_\"" } @ARGV;
+        print $dfh "
+$probe
+/$conds/
+{
+    printf(\"$dir %s %s %d %s\\n\",
+        args[2]->ip_saddr,
+        args[2]->ip_daddr,
+        args[2]->ip_plength,
+        args[3]->if_name);
+}\n";
+    };
+    ipprobe "ip:::send", ">";
+    ipprobe "ip:::receive", "<";
+};
+
+my $height = (defined $ENV{DMON_HEIGHT}) ? $ENV{DMON_HEIGHT} : 40;
+$height--;
+
+########################################################################
+
+my @sizePrefix = ("E", "P", "T", "G", "M", "K");
+my @countBound = (1e18, 1e15, 1e12, 1e9, 1e6, 1e3);
+my @sizeBound = (1<<60, 1<<50, 1<<40, 1<<30, 1<<20, 1<<10);
+
+sub human {
+    my ($s, $suffix, $bounds) = @_;
+    foreach my $i (0 .. $#{$bounds}) {
+        next if $s < @{$bounds}[$i];
+        return sprintf "%.2f %s%s", $s / @{$bounds}[$i], $sizePrefix[$i], $suffix;
+    };
+    return "$s $suffix";
+}
+
+sub humanCount { return human shift, "", \@countBound }
+
+sub humanSize { return human shift, "iB", \@sizeBound }
+#
+########################################################################
+
+my (%tx, %rx);
+my ($pktsRx, $pktsTx, $bytesRx, $bytesTx) = (0, 0, 0, 0);
+format STDOUT_TOP =
+@>>>>>>>>>> / @<<<<<<<<<<                  @>>>>>>>>>>>> / @<<<<<<<<<<<<
+humanCount($pktsRx), humanCount($pktsTx), humanSize($bytesRx), humanSize($bytesTx)
+------------------------------------------------------------------------
+.
+
+my ($left, $dir, $right, $size);
+format STDOUT =
+@<<<<<<<<<<<<<<<<<<<<<<<<<< @| @>>>>>>>>>>>>>>>>>>>>>>>>>> @>>>>>>>>>>>>
+$left, $dir, $right, humanSize($size)
+.
+
+########################################################################
+
+my $dexec;
+open $dexec, "-|", "dtrace -s $dfn" or die "$!";
+sleep 1;
+unlink $dfn;
+
+my $xx;
+while (<$dexec>) {
+    next unless /^[<>T]/;
+    my @cols = split;
+    if ($cols[0] eq "T") {
+        my @res;
+        foreach my $src (keys %rx) {
+            foreach my $dst (keys %{$rx{$src}}) {
+                push @res, [$dst, "<", $src, $rx{$src}{$dst}];
+            };
+        };
+        foreach my $src (keys %tx) {
+            foreach my $dst (keys %{$tx{$src}}) {
+                push @res, [$src, ">", $dst, $tx{$src}{$dst}];
+            };
+        };
+        @res = sort { @{$b}[-1] <=> @{$a}[-1] } @res;
+        printf "\033[H\033[J";
+        $- = 0;
+        foreach (@res[0..$height]) {
+            last unless defined;
+            ($left, $dir, $right, $size) = @{$_};
+            write STDOUT;
+        };
+        (%rx, %tx) = (), ();
+        next;
+    };
+    if ($cols[0] eq "<") {
+        $xx = \%rx;
+        $pktsRx++;
+        $bytesRx += $cols[3];
+    } else {
+        $xx = \%tx;
+        $pktsTx++;
+        $bytesTx += $cols[3];
+    };
+    if (defined ${$xx}{$cols[1]}) {
+        ${$xx}{$cols[1]}{$cols[2]} += $cols[3];
+    } else {
+        ${$xx}{$cols[1]} = {$cols[2] => $cols[3]};
+    };
+};