Profile graphs
This commit is contained in:
parent
0335dac6d4
commit
a81a01f394
5 changed files with 115 additions and 56 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
||||||
netreplay
|
netreplay
|
||||||
|
__pycache__
|
||||||
rpxy_*
|
rpxy_*
|
||||||
|
|
|
||||||
36
README.md
36
README.md
|
|
@ -261,7 +261,11 @@ sudo chmod +s /sbin/sa
|
||||||
Install OpenSSL with debug symbols:
|
Install OpenSSL with debug symbols:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./Configure --release -g
|
#./Configure --release -g
|
||||||
|
# Options from Debian build
|
||||||
|
# Debian package libssl3t64 -> Developer Information -> buildd reproducibility -> trixie rbuild
|
||||||
|
# https://tests.reproducible-builds.org/debian/rb-pkg/trixie/amd64/openssl.html
|
||||||
|
/usr/bin/perl ./Configure --release -g --prefix=/usr --openssldir=/usr/lib/ssl --libdir=lib/x86_64-linux-gnu shared no-idea no-mdc2 no-rc5 no-ssl3 no-ssl3-method enable-rfc3779 enable-cms no-capieng no-rdrand enable-tfo enable-zstd enable-zlib enable-fips enable-ec_nistp_64_gcc_128
|
||||||
```
|
```
|
||||||
|
|
||||||
Backup your system's `libcrypto.so` and `libssl.so` and replace them with the new ones.
|
Backup your system's `libcrypto.so` and `libssl.so` and replace them with the new ones.
|
||||||
|
|
@ -272,40 +276,12 @@ python exp.py make local -c
|
||||||
python exp.py send local
|
python exp.py send local
|
||||||
python exp.py update-certs
|
python exp.py update-certs
|
||||||
python exp.py run local
|
python exp.py run local
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Problems
|
## Problems
|
||||||
|
|
||||||
### Youtube gives 502 bad gateway.
|
|
||||||
|
|
||||||
* Works with bare curl. (not hiding we're a bot)
|
|
||||||
* We have same JA3 fingerprint as Firefox.
|
|
||||||
* HTTP request is intact.
|
|
||||||
* JA4 fingerprint different from Firefox but existing for some browsers.
|
|
||||||
|
|
||||||
### Modèle d'expérience à revoir
|
|
||||||
|
|
||||||
* Le relai d'une vidéo streaming avec TLS prend max 3% d'un cœur sur le Pi3, soit pas beaucoup plus que le bruit.
|
|
||||||
* Il faudrait spammer avec plusieurs connexions pour voir un effet significatif.
|
|
||||||
* On peut spammer le streaming vidéo, mais pas le reste (antibots).
|
|
||||||
* On ne devrait pas spammer sur Renater...
|
|
||||||
* Il faut donc tout faire en local.
|
|
||||||
|
|
||||||
Solutions :
|
|
||||||
|
|
||||||
* Copier les sites en statique et les servir avec Apache. => OK pour des sites propres genre Wikipedia, WordPress (à voir pour la pub)
|
|
||||||
* Installer des instances => Peertube, WordPress
|
|
||||||
* Copier le trafic et le rejouer => risque de demander beaucoup de dev
|
|
||||||
|
|
||||||
### Youtube
|
### Youtube
|
||||||
|
|
||||||
Youtube utilise des trucs aléatoires en `RANDOM.googlevideo.com` pour la vidéo. Cependant il y a quelques domaines utilisés qui ne changent pas, du moins sur un même navigateur avec la même vidéo et sur une courte période.
|
Youtube utilise des trucs aléatoires en `RANDOM.googlevideo.com` pour la vidéo. Cependant il y a quelques domaines utilisés qui ne changent pas, du moins sur un même navigateur avec la même vidéo et sur une courte période.
|
||||||
Avant d'enregistrer le trafic, il faut observer les domaines utilisés puis générer les certificats et les redirections en fonction.
|
Avant d'enregistrer le trafic, il faut observer les domaines utilisés puis générer les certificats et les redirections en fonction.
|
||||||
|
|
||||||
## TODO
|
|
||||||
|
|
||||||
* Partie serveur sans TLS de netreplay -> sans SNI, il faut parser le HTTP >.<
|
|
||||||
* exp.py: détecter la fin du replay
|
|
||||||
* yoctowatt
|
|
||||||
* mesures CPU, mémoire, bande passante sur p2
|
|
||||||
* CPU: paquet acct, commande `sa -m`
|
|
||||||
|
|
|
||||||
27
exp.py
27
exp.py
|
|
@ -33,13 +33,14 @@ CONFIGS = {
|
||||||
"setups": [
|
"setups": [
|
||||||
"none-local",
|
"none-local",
|
||||||
"client-local",
|
"client-local",
|
||||||
#"server-local",
|
"server-local",
|
||||||
],
|
],
|
||||||
"p2_hostname": "localhost",
|
"p2_hostname": "localhost",
|
||||||
"p2_addr": "127.0.0.1",
|
"p2_addr": "127.0.0.1",
|
||||||
"p2_repodir": "/home/tuxmain/reps/tlsbench",
|
"p2_repodir": "/home/tuxmain/reps/tlsbench",
|
||||||
"wattmeter": False,
|
"wattmeter": False,
|
||||||
"perf": True,
|
"perf": True,
|
||||||
|
"perf_dir": "/home/tuxmain/.cache/exp",
|
||||||
"p3_suffix": ".localhost",
|
"p3_suffix": ".localhost",
|
||||||
"p3_port_plain": 8080,
|
"p3_port_plain": 8080,
|
||||||
"p3_port_tls": 8443,
|
"p3_port_tls": 8443,
|
||||||
|
|
@ -75,7 +76,7 @@ DOMAINS_ = [
|
||||||
]
|
]
|
||||||
|
|
||||||
RECORDS = [
|
RECORDS = [
|
||||||
{ "filename": "youtube", "repeat": 2 },
|
{ "filename": "youtube", "repeat": 1 },
|
||||||
#{ "filename": "peertube", "repeat": 10 },
|
#{ "filename": "peertube", "repeat": 10 },
|
||||||
#{ "filename": "wikipedia", "repeat": 1 },
|
#{ "filename": "wikipedia", "repeat": 1 },
|
||||||
#{ "filename": "apple", "repeat": 1000 },
|
#{ "filename": "apple", "repeat": 1000 },
|
||||||
|
|
@ -90,8 +91,8 @@ CERT_SIGN_ALGS = [
|
||||||
IMPLS = [
|
IMPLS = [
|
||||||
"aws_lc_rs", # Amazon's Rust crypto widely used in Rust stuff
|
"aws_lc_rs", # Amazon's Rust crypto widely used in Rust stuff
|
||||||
#"boring", # Google's fork of OpenSSL used in Chrome and Android
|
#"boring", # Google's fork of OpenSSL used in Chrome and Android
|
||||||
"openssl", # widely used
|
#"openssl", # widely used
|
||||||
"ring", # used in most Rust stuff
|
#"ring", # used in most Rust stuff
|
||||||
#"symcrypt", # Microsoft's crypto
|
#"symcrypt", # Microsoft's crypto
|
||||||
#"wolfcrypt" # used in embedded (won't build with rpxy for now)
|
#"wolfcrypt" # used in embedded (won't build with rpxy for now)
|
||||||
]
|
]
|
||||||
|
|
@ -519,9 +520,14 @@ def run_exp(expdir, config, only_record=None, idle=False):
|
||||||
logfile_name = "log-"+timestr
|
logfile_name = "log-"+timestr
|
||||||
logfile_path = expdir+"/"+logfile_name
|
logfile_path = expdir+"/"+logfile_name
|
||||||
logfile = open(logfile_path, "w")
|
logfile = open(logfile_path, "w")
|
||||||
logfile.write("exp impl alg kex cipher setup record time cpu bytes_in bytes_out Wh\n")
|
logfile.write("exp impl alg kex cipher setup record time cpu bytes_in bytes_out Wh prof\n")
|
||||||
logfile.close()
|
logfile.close()
|
||||||
|
|
||||||
|
perf_dir = ""
|
||||||
|
if config["perf"]:
|
||||||
|
perf_dir = config["perf_dir"]
|
||||||
|
os.makedirs(perf_dir, exist_ok=True)
|
||||||
|
|
||||||
if idle:
|
if idle:
|
||||||
print("Measuring idle...")
|
print("Measuring idle...")
|
||||||
rpxy_cpu = get_cpu_stat(ssh)
|
rpxy_cpu = get_cpu_stat(ssh)
|
||||||
|
|
@ -547,7 +553,7 @@ def run_exp(expdir, config, only_record=None, idle=False):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
with open(logfile_path, "a") as logfile:
|
with open(logfile_path, "a") as logfile:
|
||||||
logfile.write(f"idle - - - - - - {time_diff} {rpxy_cpu_diff} {p2_bytes_in_diff} {p2_bytes_out_diff} {energy_diff}\n")
|
logfile.write(f"idle - - - - - - {time_diff} {rpxy_cpu_diff} {p2_bytes_in_diff} {p2_bytes_out_diff} {energy_diff} -\n")
|
||||||
logfile.close()
|
logfile.close()
|
||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -582,14 +588,17 @@ def run_exp(expdir, config, only_record=None, idle=False):
|
||||||
for record in exp["records"]:
|
for record in exp["records"]:
|
||||||
print(f"EXPERIMENT {expname}: {impl} {alg} {kex} {cipher} {setup}")
|
print(f"EXPERIMENT {expname}: {impl} {alg} {kex} {cipher} {setup}")
|
||||||
p2_rpxy_config = SETUPS[setup]["rpxy_config"]
|
p2_rpxy_config = SETUPS[setup]["rpxy_config"]
|
||||||
vars = {"CIPHERS": cipher, "KEXES": kex, "RUST_LOG": "debug", "LD_PRELOAD": "/dev/shm/openssl-3.6.0/libssl.so.3 /dev/shm/openssl-3.6.0/libcrypto.so.3"}
|
vars = {"CIPHERS": cipher, "KEXES": kex, "RUST_LOG": "warning"}
|
||||||
cmd = f"{p2_path}/rpxy_rustls_{impl} --config {expdir}/configs/{p2_rpxy_config}.toml --log-dir /dev/shm"
|
cmd = f"{p2_path}/rpxy_rustls_{impl} --config {expdir}/configs/{p2_rpxy_config}.toml --log-dir /dev/shm"
|
||||||
|
#cmd = f"{p2_path}/rpxy_rustls_{impl} --config {expdir}/configs/{p2_rpxy_config}.toml"
|
||||||
ssh_run_bg(ssh, cmd, env=vars)
|
ssh_run_bg(ssh, cmd, env=vars)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
prof_filename = "-"
|
||||||
if config["perf"]:
|
if config["perf"]:
|
||||||
|
prof_filename = f"{perf_dir}/perf-{timestr}-{run_id}.data"
|
||||||
rpxy_pid = ssh_run(ssh, f"pidof rpxy_rustls_{impl}").removesuffix("\n")
|
rpxy_pid = ssh_run(ssh, f"pidof rpxy_rustls_{impl}").removesuffix("\n")
|
||||||
ssh_run_bg(ssh, f"perf record -F 997 --call-graph dwarf,64000 -g -o {expdir}/perf-{timestr}-{run_id}.data -p {rpxy_pid}")
|
ssh_run_bg(ssh, f"perf record -F 997 --call-graph dwarf,64000 -g -o {prof_filename} -p {rpxy_pid}")
|
||||||
|
|
||||||
run_id += 1
|
run_id += 1
|
||||||
|
|
||||||
|
|
@ -648,7 +657,7 @@ def run_exp(expdir, config, only_record=None, idle=False):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
with open(logfile_path, "a") as logfile:
|
with open(logfile_path, "a") as logfile:
|
||||||
logfile.write(f"{expname} {impl} {alg} {kex} {cipher} {setup} {record_filename} {time_diff} {rpxy_cpu_diff} {p2_bytes_in_diff} {p2_bytes_out_diff} {energy_diff}\n")
|
logfile.write(f"{expname} {impl} {alg} {kex} {cipher} {setup} {record_filename} {time_diff} {rpxy_cpu_diff} {p2_bytes_in_diff} {p2_bytes_out_diff} {energy_diff} {prof_filename}\n")
|
||||||
logfile.close()
|
logfile.close()
|
||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
99
plots.py
99
plots.py
|
|
@ -1,4 +1,5 @@
|
||||||
import os, sys
|
import os, sys
|
||||||
|
import profile
|
||||||
|
|
||||||
# Nice labels for algorithms of all kinds
|
# Nice labels for algorithms of all kinds
|
||||||
ALG_LABEL = {
|
ALG_LABEL = {
|
||||||
|
|
@ -30,7 +31,8 @@ VER_LABEL = {
|
||||||
# Titles for measured quantities
|
# Titles for measured quantities
|
||||||
OBJ_TITLE = {
|
OBJ_TITLE = {
|
||||||
"cpu": "CPU time",
|
"cpu": "CPU time",
|
||||||
"energy": "energy consumption"
|
"energy": "energy consumption",
|
||||||
|
"profile": "time profile",
|
||||||
}
|
}
|
||||||
# Logfile column names
|
# Logfile column names
|
||||||
COL = {
|
COL = {
|
||||||
|
|
@ -43,7 +45,8 @@ COL = {
|
||||||
# Physical units by object
|
# Physical units by object
|
||||||
UNIT = {
|
UNIT = {
|
||||||
"cpu": "s",
|
"cpu": "s",
|
||||||
"energy": "W"
|
"energy": "W",
|
||||||
|
"profile": "samples",
|
||||||
}
|
}
|
||||||
# Titles for criteria
|
# Titles for criteria
|
||||||
CRITERION_TITLE = {
|
CRITERION_TITLE = {
|
||||||
|
|
@ -83,7 +86,31 @@ plot \
|
||||||
f.close()
|
f.close()
|
||||||
os.system("gnuplot {plots_dir}/{object}_by_{criterion}_{side}_{record}.gnuplot".format(plots_dir=PLOTS_DIR, **kwargs))
|
os.system("gnuplot {plots_dir}/{object}_by_{criterion}_{side}_{record}.gnuplot".format(plots_dir=PLOTS_DIR, **kwargs))
|
||||||
|
|
||||||
def make_plot(logs, exp, criterion, side, obj, record):
|
def gnuplot_stacked_histogram(**kwargs):
|
||||||
|
cluster = ""
|
||||||
|
#for i in range(kwargs["nb_impls"]-1):
|
||||||
|
# cluster += """, "" using {}:xticlabels(1) title col""".format(i+4)
|
||||||
|
f = open("{plots_dir}/{object}_by_{criterion}_{side}_{record}.gnuplot".format(plots_dir=PLOTS_DIR, **kwargs), "w")
|
||||||
|
f.write("""\
|
||||||
|
set terminal pngcairo enhanced font "CMU Sans Serif,11" fontscale 1.0 size 800, 600
|
||||||
|
set output "{plots_dir}/{object}_by_{criterion}_{side}_{record}.png"
|
||||||
|
set boxwidth 0.9 absolute
|
||||||
|
set style fill solid 1.0 border lt -1
|
||||||
|
set style histogram rowstacked
|
||||||
|
set style data histograms
|
||||||
|
set title font "CMU Sans Serif,12" "{object_title} by {criterion_title} ({record}, {side} side) ({unit})"
|
||||||
|
set xtics border in scale 0,0 nomirror noenhanced rotate by -15 autojustify
|
||||||
|
set key fixed right top vertical Right noenhanced autotitle nobox invert
|
||||||
|
set colorbox vertical origin screen 0.9, 0.2 size screen 0.05, 0.6 front noinvert bdefault
|
||||||
|
set xrange [ * : * ] noreverse writeback
|
||||||
|
set yrange [ 0 : * ]
|
||||||
|
set grid y lt 1 lw .75 lc "gray"
|
||||||
|
plot for [i=2:{nb_functions}] "{plots_dir}/{object}_by_{criterion}_{side}_{record}.dat" using i:xticlabels(1) title col
|
||||||
|
""".format(plots_dir=PLOTS_DIR, cluster=cluster, **kwargs))
|
||||||
|
f.close()
|
||||||
|
os.system("gnuplot {plots_dir}/{object}_by_{criterion}_{side}_{record}.gnuplot".format(plots_dir=PLOTS_DIR, **kwargs))
|
||||||
|
|
||||||
|
def make_log_plot(logs, exp, criterion, side, obj, record):
|
||||||
f = open(f"/dev/shm/plots/{obj}_by_{criterion}_{side}_{record}.dat", "w")
|
f = open(f"/dev/shm/plots/{obj}_by_{criterion}_{side}_{record}.dat", "w")
|
||||||
ciphers = {}
|
ciphers = {}
|
||||||
impls = []
|
impls = []
|
||||||
|
|
@ -102,9 +129,7 @@ def make_plot(logs, exp, criterion, side, obj, record):
|
||||||
return
|
return
|
||||||
|
|
||||||
for log in logs:
|
for log in logs:
|
||||||
if log["exp"] != exp or log["record"] != record:
|
if log["exp"] == exp and log["record"] == record and log["setup"] == side:
|
||||||
continue
|
|
||||||
elif log["setup"] == side:
|
|
||||||
if log[COL[criterion]] not in ciphers:
|
if log[COL[criterion]] not in ciphers:
|
||||||
ciphers[log[COL[criterion]]] = {}
|
ciphers[log[COL[criterion]]] = {}
|
||||||
ciphers[log[COL[criterion]]][log["impl"]] = float(log[COL[obj]]) - idle_val * float(log["time"])
|
ciphers[log[COL[criterion]]][log["impl"]] = float(log[COL[obj]]) - idle_val * float(log["time"])
|
||||||
|
|
@ -125,8 +150,43 @@ def make_plot(logs, exp, criterion, side, obj, record):
|
||||||
f.close()
|
f.close()
|
||||||
gnuplot_histogram(object=obj, criterion=criterion, side=side, object_title=OBJ_TITLE[obj], criterion_title=CRITERION_TITLE[criterion], unit=UNIT[obj], nb_impls=len(impls), record=record)
|
gnuplot_histogram(object=obj, criterion=criterion, side=side, object_title=OBJ_TITLE[obj], criterion_title=CRITERION_TITLE[criterion], unit=UNIT[obj], nb_impls=len(impls), record=record)
|
||||||
|
|
||||||
|
def make_profile_plot(logs, exp, criterion, side, record, no_flamegraph=False):
|
||||||
|
f = open(f"/dev/shm/plots/profile_by_{criterion}_{side}_{record}.dat", "w")
|
||||||
|
runs = []
|
||||||
|
functions = []
|
||||||
|
|
||||||
|
for log in logs:
|
||||||
|
if log["exp"] == exp and log["record"] == record and log["setup"] == side:
|
||||||
|
svg_filename = log["prof"] + ".svg"
|
||||||
|
if not no_flamegraph:
|
||||||
|
os.system("flamegraph --perfdata {} -o {}".format(log["prof"], svg_filename))
|
||||||
|
profile_results = profile.extract_from_file(svg_filename)
|
||||||
|
print(profile_results)
|
||||||
|
for function in profile_results:
|
||||||
|
if function not in functions:
|
||||||
|
functions.append(function)
|
||||||
|
runs.append({
|
||||||
|
criterion: log[COL[criterion]],
|
||||||
|
"impl": log["impl"],
|
||||||
|
"functions": profile_results,
|
||||||
|
})
|
||||||
|
f.write("{} {}\n".format(criterion, " ".join(functions)))
|
||||||
|
for run in runs:
|
||||||
|
f.write("\"{} {}({})\" {}\n".format(
|
||||||
|
run["impl"],
|
||||||
|
ALG_LABEL[run[criterion]],
|
||||||
|
VER_LABEL[log["cipher"]],
|
||||||
|
" ".join([
|
||||||
|
str(run["functions"][function][0])
|
||||||
|
for function in functions
|
||||||
|
]),
|
||||||
|
))
|
||||||
|
f.close()
|
||||||
|
gnuplot_stacked_histogram(object="profile", criterion=criterion, side=side, object_title=OBJ_TITLE["profile"], criterion_title=CRITERION_TITLE[criterion], unit=UNIT["profile"], record=record, nb_functions=len(functions)+1)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
logfile_name = sys.argv[1]
|
cmd = sys.argv[1]
|
||||||
|
logfile_name = sys.argv[2]
|
||||||
logfile = open(logfile_name, "r")
|
logfile = open(logfile_name, "r")
|
||||||
lines = logfile.readlines()
|
lines = logfile.readlines()
|
||||||
logfile.close()
|
logfile.close()
|
||||||
|
|
@ -146,11 +206,20 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
os.makedirs("/dev/shm/plots", exist_ok=True)
|
os.makedirs("/dev/shm/plots", exist_ok=True)
|
||||||
|
|
||||||
for side in ["client", "server"]:
|
no_flamegraph = "-f" in sys.argv
|
||||||
for record in records:
|
|
||||||
make_plot(logs, "impl-cipher-ver", "cipher", side, "cpu", record)
|
if cmd == "log":
|
||||||
make_plot(logs, "impl-cipher-ver", "cipher", side, "energy", record)
|
for side in ["client", "server"]:
|
||||||
make_plot(logs, "impl-cert-ver", "cert", side, "cpu", record)
|
for record in records:
|
||||||
make_plot(logs, "impl-cert-ver", "cert", side, "energy", record)
|
make_log_plot(logs, "impl-cipher-ver", "cipher", side, "cpu", record)
|
||||||
make_plot(logs, "impl-kex-ver", "kex", side, "cpu", record)
|
make_log_plot(logs, "impl-cipher-ver", "cipher", side, "energy", record)
|
||||||
make_plot(logs, "impl-kex-ver", "kex", side, "energy", record)
|
make_log_plot(logs, "impl-cert-ver", "cert", side, "cpu", record)
|
||||||
|
make_log_plot(logs, "impl-cert-ver", "cert", side, "energy", record)
|
||||||
|
make_log_plot(logs, "impl-kex-ver", "kex", side, "cpu", record)
|
||||||
|
make_log_plot(logs, "impl-kex-ver", "kex", side, "energy", record)
|
||||||
|
elif cmd == "prof":
|
||||||
|
for side in ["client-local", "server-local"]:
|
||||||
|
for record in records:
|
||||||
|
make_profile_plot(logs, "impl-cipher-ver", "cipher", side, record, no_flamegraph=no_flamegraph)
|
||||||
|
make_profile_plot(logs, "impl-cert-ver", "cert", side, record, no_flamegraph=no_flamegraph)
|
||||||
|
make_profile_plot(logs, "impl-kex-ver", "kex", side, record, no_flamegraph=no_flamegraph)
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,7 @@ def extract_function(data, name):
|
||||||
percents += float(match.group(2))
|
percents += float(match.group(2))
|
||||||
return (samples, percents)
|
return (samples, percents)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def extract_from_file(filename):
|
||||||
filename = sys.argv[1]
|
|
||||||
f = open(filename, "r")
|
f = open(filename, "r")
|
||||||
c = f.read()
|
c = f.read()
|
||||||
results = {}
|
results = {}
|
||||||
|
|
@ -57,5 +56,10 @@ if __name__ == "__main__":
|
||||||
results[title] = [0, 0.0]
|
results[title] = [0, 0.0]
|
||||||
results[title][0] += samples
|
results[title][0] += samples
|
||||||
results[title][1] += percents
|
results[title][1] += percents
|
||||||
|
return results
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
filename = sys.argv[1]
|
||||||
|
results = extract_from_file(filename)
|
||||||
for title in results:
|
for title in results:
|
||||||
print(title, results[title])
|
print(title, results[title])
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue