aboutsummaryrefslogtreecommitdiff
path: root/tools/dtrace-tool.pl
blob: 693a9e75077a6f4f280ef20c8059c1e9f87eec5d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#!/usr/bin/env perl

# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# Basic script to run dtrace sampling over the program (requires Flamegraph)
# Usage is either running as: ./dtrace-tool.sh pid (then input the pid of the process)
# Or just run directly with: ./dtrace-tool.sh <command>
use strict;
use warnings;
use POSIX qw(strftime);

my $input;
my $sampling_hz = '997';
my $sampling_time = '60';
my $sampling_pid = `pgrep eden`;
chomp($sampling_pid);

my $sampling_program = 'eden';
my $sampling_type = 0;

sub dtrace_ask_params {
    my $is_ok = 'Y';
    do {
        print "Sampling HZ [" . $sampling_hz . "]: ";
        chomp($input = <STDIN>);
        $sampling_hz = $input || $sampling_hz;

        print "Sampling time [" . $sampling_time . "]: ";
        chomp($input = <STDIN>);
        $sampling_time = $input || $sampling_time;

        print "Sampling pid [" . $sampling_pid . "]: ";
        chomp($input = <STDIN>);
        $sampling_pid = $input || $sampling_pid;

        print "Are these settings correct?: [" . $is_ok . "]\n";
        print "HZ = " . $sampling_hz . "\nTime = " . $sampling_time . "\nPID = " . $sampling_pid . "\n";
        chomp($input = <STDIN>);
        $is_ok = $input || $is_ok;
    } while ($is_ok eq 'n');
}

sub dtrace_probe_profiling {
    if ($sampling_type eq 0) {
        # profile both kernel stacks and user stacks
        return "
profile-".$sampling_hz." /pid == ".$sampling_pid." && arg0/ { @[stack(100)] = count(); }
profile-".$sampling_hz." /pid == ".$sampling_pid." && arg1/ { @[ustack(100)] = count(); }
tick-".$sampling_time."s { exit(0); }";
    } elsif ($sampling_type eq 1) {
        # trace syscall entries
        return "
syscall:::entry /pid == ".$sampling_pid."/ { \@traces[ustack(100)] = count(); }
tick-".$sampling_time."s { exit(0); }";
    } elsif ($sampling_type eq 2) {
        # profile both kernel and user stacks with thread names attached
        return "
profile-".$sampling_hz." /pid == ".$sampling_pid." && arg0/ { @[stringof(curthread->td_name), stack(100)] = count(); }
profile-".$sampling_hz." /pid == ".$sampling_pid." && arg1/ { @[stringof(curthread->td_name), ustack(100)] = count(); }
tick-".$sampling_time."s { exit(0); }";
    } elsif ($sampling_type eq 3) {
        # trace I/O requests
        return "
io::start /pid == ".$sampling_pid."/ { @[ustack(100)] = count(); }
tick-".$sampling_time."s { exit(0); }";
    } else {
        die "idk";
    }
}

sub dtrace_generate {
    my @date = (localtime(time))[5, 4, 3, 2, 1, 0];
    $date[0] += 1900;
    $date[1]++;
    my $fmt_date = sprintf "%4d-%02d-%02d_%02d-%02d-%02d", @date;
    my $trace_dir = "dtrace-out";
    my $trace_file = $trace_dir . "/" . $fmt_date . ".user_stacks";
    my $trace_fold = $trace_dir . "/" . $fmt_date . ".fold";
    my $trace_svg = $trace_dir . "/" . $fmt_date . ".svg";
    my $trace_probe = dtrace_probe_profiling;

    print $trace_probe . "\n";
    system "sudo", "dtrace", "-Z", "-n", $trace_probe, "-o", $trace_file;
    die "$!" if $?;

    open (my $trace_fold_handle, ">", $trace_fold) or die "$!";
    #run ["perl", "../FlameGraph/stackcollapse.pl", $trace_file], ">", \my $fold_output;
    my $fold_output = `perl ../FlameGraph/stackcollapse.pl $trace_file`;
    print $trace_fold_handle $fold_output;

    open (my $trace_svg_handle, ">", $trace_svg) or die "$!";
    #run ["perl", "../FlameGraph/flamegraph.pl", $trace_fold], ">", \my $svg_output;
    my $svg_output = `perl ../FlameGraph/flamegraph.pl $trace_fold`;
    print $trace_svg_handle $svg_output;

    system "sudo", "chmod", "0666", $trace_file;
}

foreach my $i (0 .. $#ARGV) {
    if ($ARGV[$i] eq '-h') {
        print "
Usage: $0\n
-p          Prompt for parameters\n
-g          Generate dtrace output\n
-s          Continously generate output until Ctrl^C\n
-<n>        Select dtrace type\n
            0: Profile kernel + user stacks (default)\n
            1: Trace syscall entries\n
            2: Profile kernel + user stacks with thread names\n
            3: Trace I/O requests\n
";
    } elsif ($ARGV[$i] eq '-g') {
        dtrace_generate;
    } elsif ($ARGV[$i] eq '-s') {
        while (1) {
            dtrace_generate;
        }
    } elsif ($ARGV[$i] eq '-p') {
        dtrace_ask_params;
    } else {
        $sampling_type = substr $ARGV[$i], 1;
        print "Select: ".$sampling_type."\n";
    }
}