Compare commits
5 Commits
master
...
nin/usdt-r
Author | SHA1 | Date |
---|---|---|
Félix Baylac-Jacqué | e8454af75b | |
Félix Baylac-Jacqué | 62c1bd0247 | |
Félix Baylac-Jacqué | 1fdab971b3 | |
Félix Baylac-Jacqué | 9b35ebe945 | |
Félix Baylac-Jacqué | 0914a36b9a |
|
@ -127,3 +127,5 @@ nix-rust/target
|
|||
result
|
||||
|
||||
.vscode/
|
||||
|
||||
target/
|
|
@ -0,0 +1,12 @@
|
|||
#include <linux/string.h>
|
||||
|
||||
struct trace_event {
|
||||
u64 ts;
|
||||
u64 expr_id;
|
||||
u32 line;
|
||||
u32 column;
|
||||
char probe_name[25];
|
||||
char file[128];
|
||||
};
|
||||
|
||||
BPF_PERF_OUTPUT(events);
|
|
@ -0,0 +1,104 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p python3 bcc --pure
|
||||
|
||||
from bcc import BPF, USDT
|
||||
import ctypes as ct
|
||||
import os
|
||||
import sys
|
||||
import io
|
||||
|
||||
|
||||
if len(sys.argv) < 1:
|
||||
print("USAGE: ebpf-trace LIBRARY_PATH")
|
||||
exit()
|
||||
|
||||
debug = False
|
||||
|
||||
lib_path = sys.argv[1]
|
||||
|
||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(os.path.join(script_dir, 'ebpf-trace.c')) as bpf_file:
|
||||
bpf_text = bpf_file.read()
|
||||
|
||||
def generate_bpf_function_for_probe_in(probe_name):
|
||||
return f"""
|
||||
int usdt_trace_in_{probe_name} (struct pt_regs *ctx) {{
|
||||
uint64_t addr;
|
||||
struct trace_event trace_event = {{0}};
|
||||
|
||||
trace_event.ts = bpf_ktime_get_ns();
|
||||
|
||||
bpf_usdt_readarg(1, ctx, &trace_event.expr_id);
|
||||
|
||||
bpf_usdt_readarg(2, ctx, &trace_event.line);
|
||||
|
||||
bpf_usdt_readarg(3, ctx, &trace_event.column);
|
||||
|
||||
bpf_usdt_readarg(4, ctx, &addr);
|
||||
bpf_probe_read_user_str(&trace_event.file, sizeof(char) * 128, (void *)addr);
|
||||
|
||||
strcpy(trace_event.probe_name, "{probe_name}");
|
||||
|
||||
events.perf_submit(ctx, &trace_event, sizeof(struct trace_event));
|
||||
return 0;
|
||||
}};
|
||||
"""
|
||||
|
||||
def generate_bpf_function_for_probe_out(probe_name):
|
||||
return f"""
|
||||
int usdt_trace_out_{probe_name} (struct pt_regs *ctx) {{
|
||||
uint64_t addr;
|
||||
struct trace_event trace_event = {{0}};
|
||||
|
||||
trace_event.ts = bpf_ktime_get_ns();
|
||||
|
||||
bpf_usdt_readarg(1, ctx, &trace_event.expr_id);
|
||||
|
||||
events.perf_submit(ctx, &trace_event, sizeof(struct trace_event));
|
||||
|
||||
return 0;
|
||||
}};
|
||||
"""
|
||||
|
||||
probes = [ "top_level", "attrs", "let", "list", "var", "select",
|
||||
"lambda", "with", "if", "assert",
|
||||
"op_update", "call", "has_attr" ]
|
||||
probes_in_out = []
|
||||
|
||||
probes_in = [f"{probe}__in" for probe in probes]
|
||||
|
||||
probes_out = [f"{probe}__out" for probe in probes] + [
|
||||
"call_throwned__out", "has_attr_failed__out", "op_update_empty1__out",
|
||||
"op_update_empty2__out", "select_short__out"
|
||||
]
|
||||
|
||||
u = USDT(path=lib_path)
|
||||
out_file = open('ebpf_trace.txt', 'wb', 100*(2**20))
|
||||
|
||||
def print_event(cpu, data, size):
|
||||
event = b["events"].event(data)
|
||||
out_file.write(f"{event.ts} {event.expr_id} {event.probe_name.decode('ascii', errors='ignore')} {event.line}:{event.column} {event.file.decode('ascii', errors='ignore')}\n".encode("utf-8"))
|
||||
|
||||
for probe in probes_in:
|
||||
bpf_text += generate_bpf_function_for_probe_in(probe)
|
||||
u.enable_probe(probe=probe, fn_name=f"usdt_trace_in_{probe}")
|
||||
|
||||
for probe in probes_out:
|
||||
bpf_text += generate_bpf_function_for_probe_out(probe)
|
||||
u.enable_probe(probe=probe, fn_name=f"usdt_trace_out_{probe}")
|
||||
|
||||
if debug:
|
||||
print(bpf_text)
|
||||
print(probes_in_out)
|
||||
|
||||
b = BPF(text=bpf_text, usdt_contexts=[u])
|
||||
|
||||
b["events"].open_perf_buffer(print_event, page_cnt=1024)
|
||||
|
||||
print("Ready", file=sys.stderr)
|
||||
while True:
|
||||
try:
|
||||
b.perf_buffer_poll()
|
||||
except KeyboardInterrupt:
|
||||
out_file.close()
|
||||
exit()
|
|
@ -0,0 +1,87 @@
|
|||
use std::io;
|
||||
use std::io::BufRead;
|
||||
|
||||
#[derive (PartialEq, Debug)]
|
||||
enum Direction {
|
||||
In,
|
||||
Out
|
||||
}
|
||||
|
||||
#[derive (PartialEq, Debug)]
|
||||
struct Event {
|
||||
ts: u64,
|
||||
line: u64,
|
||||
id: u64,
|
||||
probe_name: String,
|
||||
probe_direction: Direction,
|
||||
line_col: String,
|
||||
filename: String
|
||||
}
|
||||
|
||||
fn main () {
|
||||
let mut buf = String::new();
|
||||
let mut stdin_h = io::stdin().lock();
|
||||
let mut done = false;
|
||||
let mut stack: Vec<Event> = Vec::new();
|
||||
let mut line_nb: u64 = 1;
|
||||
while !done {
|
||||
match stdin_h.read_line(&mut buf) {
|
||||
Ok(0) => done = true,
|
||||
Ok(_) => process_line(&buf, &mut stack, &line_nb),
|
||||
Err(_err) => {
|
||||
panic!("Error while reading from stdin.");
|
||||
}
|
||||
}
|
||||
line_nb += 1;
|
||||
buf.clear();
|
||||
}
|
||||
eprintln!("NB lines: {}", line_nb);
|
||||
}
|
||||
|
||||
fn print_stack_names (stack: &Vec<Event>) -> String {
|
||||
let mut names_str = String::new();
|
||||
for event in stack {
|
||||
names_str.push_str(format!(";{}:{}:{}", &event.probe_name, &event.filename, &event.line_col).as_str());
|
||||
};
|
||||
names_str
|
||||
}
|
||||
|
||||
fn process_line(line: &str, stack: &mut Vec<Event>, line_nb: &u64) {
|
||||
let elems: Vec<&str> = line.split(' ').collect();
|
||||
let probe_elems: Vec<&str> = elems[2].split("__").collect();
|
||||
let probe_direction = match probe_elems[1] {
|
||||
"in" => Direction::In,
|
||||
"out" => Direction::Out,
|
||||
x => panic!("Unknown probe direction {}", x)
|
||||
};
|
||||
let mut filename = String::from(elems[4]);
|
||||
filename.truncate(filename.len() - 1);
|
||||
let event = Event {
|
||||
ts: elems[0].parse().expect(format!("Cannot parse timestamp for line {}", line_nb).as_str()),
|
||||
id: elems[1].parse().expect(format!("Cannot parse probe direction for line {}", line_nb).as_str()),
|
||||
line: line_nb.clone(),
|
||||
probe_name: String::from(probe_elems[0]),
|
||||
probe_direction,
|
||||
line_col: String::from(elems[3]),
|
||||
filename
|
||||
};
|
||||
|
||||
if event.probe_direction == Direction::In {
|
||||
stack.push(event);
|
||||
} else {
|
||||
let in_event = stack.pop().expect("Error: cannot pop stack, we lack a in event.");
|
||||
if !same_frame(&event, &in_event) {
|
||||
eprintln!("Weird trace!! We found a unmatched out event for");
|
||||
eprintln!("{:?}", in_event);
|
||||
eprintln!("{:?}", event);
|
||||
stack.push(in_event);
|
||||
panic!();
|
||||
}
|
||||
let dur = event.ts - in_event.ts;
|
||||
println!("{} {}", print_stack_names(&stack), dur);
|
||||
}
|
||||
}
|
||||
|
||||
fn same_frame(a: &Event, b: &Event) -> bool {
|
||||
a.id == b.id
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bcc"
|
||||
version = "0.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce860f38082f1544a557dfa447838143e1b0bfa061c0369e407ebadf640001d1"
|
||||
dependencies = [
|
||||
"bcc-sys",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"libc",
|
||||
"socket2",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bcc-sys"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f40afb3abbf90895dda3ddbc6d8734d24215130a22d646067690f5e318f81bc"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
version = "3.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d91974fbbe88ec1df0c24a4f00f99583667a7e2e6272b2b92d294d81e462173"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.134"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test-rust"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bcc",
|
||||
"ctrlc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "test-rust"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bcc = "*"
|
||||
ctrlc = "*"
|
|
@ -0,0 +1,6 @@
|
|||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
pkgs.mkShell {
|
||||
nativeBuildInputs = [ pkgs.rustc pkgs.cargo ] ++ pkgs.bcc.nativeBuildInputs ;
|
||||
buildInputs = [ pkgs.bcc ];
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
int some_func(void *ctx){
|
||||
bpf_trace_printk("Hello, World!\\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
use bcc::{BccError, BPFBuilder, USDTContext};
|
||||
use bcc::perf_event::PerfMapBuilder;
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::ptr;
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
#[repr(C)]
|
||||
struct trace_event {
|
||||
ts: u32,
|
||||
expr_id: u32,
|
||||
line: u32,
|
||||
column: u32,
|
||||
probe_name: [u8; 25],
|
||||
file: [u8; 128]
|
||||
}
|
||||
|
||||
fn create_usdt_code_in(usdt_name: &str) -> String {
|
||||
format!("
|
||||
int usdt_trace_in_{probe_name} (struct pt_regs *ctx) {{
|
||||
uint64_t addr;
|
||||
struct trace_event trace_event = {{0}};
|
||||
|
||||
trace_event.ts = bpf_ktime_get_ns();
|
||||
|
||||
bpf_usdt_readarg(1, ctx, &trace_event.expr_id);
|
||||
|
||||
bpf_usdt_readarg(2, ctx, &trace_event.line);
|
||||
|
||||
bpf_usdt_readarg(3, ctx, &trace_event.column);
|
||||
|
||||
bpf_usdt_readarg(4, ctx, &addr);
|
||||
bpf_probe_read_user_str(&trace_event.file, sizeof(char) * 128, (void *)addr);
|
||||
|
||||
strcpy(trace_event.probe_name, \"{probe_name}\");
|
||||
|
||||
events.perf_submit(ctx, &trace_event, sizeof(struct trace_event));
|
||||
return 0;
|
||||
}}
|
||||
", probe_name = usdt_name)
|
||||
}
|
||||
|
||||
fn parse_trace_event(x: &[u8]) -> trace_event {
|
||||
unsafe { ptr::read_unaligned(x.as_ptr() as *const trace_event) }
|
||||
}
|
||||
|
||||
fn create_usdt_code_out(usdt_name: &str) -> String {
|
||||
format!("
|
||||
int usdt_trace_out_{probe_name} (struct pt_regs *ctx) {{
|
||||
uint64_t addr;
|
||||
struct trace_event trace_event = {{0}};
|
||||
|
||||
trace_event.ts = bpf_ktime_get_ns();
|
||||
|
||||
bpf_usdt_readarg(1, ctx, &trace_event.expr_id);
|
||||
|
||||
bpf_usdt_readarg(2, ctx, &trace_event.line);
|
||||
|
||||
bpf_usdt_readarg(3, ctx, &trace_event.column);
|
||||
|
||||
bpf_usdt_readarg(4, ctx, &addr);
|
||||
bpf_probe_read_user_str(&trace_event.file, sizeof(char) * 128, (void *)addr);
|
||||
|
||||
strcpy(trace_event.probe_name, \"{probe_name}\");
|
||||
|
||||
events.perf_submit(ctx, &trace_event, sizeof(struct trace_event));
|
||||
return 0;
|
||||
}}
|
||||
", probe_name = usdt_name)
|
||||
}
|
||||
|
||||
fn profiler_data_callback () -> Box<dyn FnMut(&[u8]) + Send> {
|
||||
let f = File::create("/tmp/out.txt").unwrap();
|
||||
Box::new(move |x| {
|
||||
let data = parse_trace_event(x);
|
||||
f.write_all(data.ts);
|
||||
//println!("{}", data.ts);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fn do_main(runnable: Arc<AtomicBool>, path: &str) -> Result<(), BccError> {
|
||||
let cpus = bcc::cpuonline::get()?.len() as u32;
|
||||
let mut u = USDTContext::from_binary_path(path)?;
|
||||
|
||||
let probes =
|
||||
vec! [
|
||||
"top_level".to_string(),
|
||||
"attrs".to_string(),
|
||||
"let".to_string(),
|
||||
"list".to_string(),
|
||||
"var".to_string(),
|
||||
"select".to_string(),
|
||||
"lambda".to_string(),
|
||||
"with".to_string(),
|
||||
"if".to_string(),
|
||||
"assert".to_string(),
|
||||
"op_update".to_string(),
|
||||
"call".to_string(),
|
||||
"has_attr".to_string()
|
||||
];
|
||||
|
||||
let mut probes_in = Vec::with_capacity(13);
|
||||
for probe in &probes {
|
||||
probes_in.push(format!("{}__in", probe));
|
||||
};
|
||||
|
||||
let mut probes_out = Vec::with_capacity(20);
|
||||
for probe in &probes {
|
||||
probes_out.push(format!("{}__out", probe));
|
||||
}
|
||||
probes_out.append(&mut vec![
|
||||
"call_throwned__out".to_string(),
|
||||
"has_attr_failed__out".to_string(),
|
||||
"op_update_empty1__out".to_string(),
|
||||
"op_update_empty2__out".to_string(),
|
||||
"select_short__out".to_string()
|
||||
]);
|
||||
let mut code = format!(
|
||||
"{}\n{}\n",
|
||||
format!("#define NUM_CPU {}", cpus),
|
||||
include_str!("usdt_profiler_intro.c").to_string()
|
||||
);
|
||||
for probe in &probes_in {
|
||||
code.push_str(&create_usdt_code_in(probe));
|
||||
u.enable_probe(probe, format!("usdt_trace_in_{}", probe))?;
|
||||
}
|
||||
// for probe in &probes_out {
|
||||
// code.push_str(&create_usdt_code_out(probe));
|
||||
// u.enable_probe(probe, format!("usdt_trace_out_{}", probe))?;
|
||||
// }
|
||||
|
||||
println!("Starting with code:\n{}", &code);
|
||||
let bpf = BPFBuilder::new(&code)?
|
||||
.add_usdt_context(u)?
|
||||
.build()?;
|
||||
let table = bpf.table("events")?;
|
||||
let mut perf_map = PerfMapBuilder::new(table, profiler_data_callback).page_count(128).build()?;
|
||||
println!("{}", probes_in.len() + probes_out.len());
|
||||
println!("Ready");
|
||||
while runnable.load(Ordering::SeqCst) {
|
||||
perf_map.poll(200);
|
||||
};
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let path = &args[1];
|
||||
let runnable = Arc::new(AtomicBool::new(true));
|
||||
let r = runnable.clone();
|
||||
ctrlc::set_handler(move || {
|
||||
r.store(false, Ordering::SeqCst);
|
||||
})
|
||||
.expect("Failed to set handler for SIGINT / SIGTERM");
|
||||
|
||||
if let Err(x) = do_main(runnable, path) {
|
||||
eprintln!("Error: {}", x);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#include <linux/string.h>
|
||||
|
||||
struct trace_event {
|
||||
u32 ts;
|
||||
u32 expr_id;
|
||||
u32 line;
|
||||
u32 column;
|
||||
char probe_name[25];
|
||||
char file[128];
|
||||
};
|
||||
|
||||
BPF_PERF_OUTPUT(events);
|
|
@ -0,0 +1,2 @@
|
|||
#!/usr/bin/env bash
|
||||
nix-shell --run "cargo build --release && sudo ./target/release/test-rust /home/ninjatrappeur/code-root/github.com/NixOS/nix/outputs/out/lib/libnixexpr.so"
|
|
@ -115,6 +115,7 @@
|
|||
boost
|
||||
lowdown-nix
|
||||
gtest
|
||||
libsystemtap
|
||||
]
|
||||
++ lib.optionals stdenv.isLinux [libseccomp]
|
||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <functional>
|
||||
|
||||
#include <sys/resource.h>
|
||||
#include <sys/sdt.h>
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
|
||||
|
@ -35,6 +36,17 @@
|
|||
|
||||
#endif
|
||||
|
||||
#define DTRACE_EVAL_IN(evalState, probeName) \
|
||||
Pos dtrace_pos = evalState.positions[this->getPos()]; \
|
||||
long exprId = evalState.exprId; \
|
||||
evalState.exprId++; \
|
||||
DTRACE_PROBE4(nix, probeName, exprId, dtrace_pos.line, dtrace_pos.column, dtrace_pos.file.c_str());
|
||||
|
||||
// note: DTRACE_EVAL_OUT should *always* be expanded in a scope where
|
||||
// a DTRACE_EVAL_IN has already been expanded.
|
||||
#define DTRACE_EVAL_OUT(probeName, exprId) \
|
||||
DTRACE_PROBE1(nix, probeName, exprId);
|
||||
|
||||
namespace nix {
|
||||
|
||||
static char * allocString(size_t size)
|
||||
|
@ -468,6 +480,7 @@ EvalState::EvalState(
|
|||
, debugStop(false)
|
||||
, debugQuit(false)
|
||||
, trylevel(0)
|
||||
, exprId(0)
|
||||
, regexCache(makeRegexCache())
|
||||
#if HAVE_BOEHMGC
|
||||
, valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
|
||||
|
@ -1269,7 +1282,14 @@ void EvalState::cacheFile(
|
|||
|
||||
void EvalState::eval(Expr * e, Value & v)
|
||||
{
|
||||
auto dtrace_pos = this->positions[e->getPos()];
|
||||
long exprId = this->exprId;
|
||||
this->exprId++;
|
||||
DTRACE_PROBE4(nix, top_level__in, exprId, dtrace_pos.line, dtrace_pos.column, dtrace_pos.file.c_str());
|
||||
|
||||
e->eval(*this, baseEnv, v);
|
||||
|
||||
DTRACE_EVAL_OUT(top_level__out, exprId);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1332,6 +1352,8 @@ void ExprPath::eval(EvalState & state, Env & env, Value & v)
|
|||
|
||||
void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
DTRACE_EVAL_IN(state, attrs__in);
|
||||
|
||||
v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish());
|
||||
auto dynamicEnv = &env;
|
||||
|
||||
|
@ -1411,11 +1433,15 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
|||
}
|
||||
|
||||
v.attrs->pos = pos;
|
||||
|
||||
DTRACE_EVAL_OUT(attrs__out, exprId);
|
||||
}
|
||||
|
||||
|
||||
void ExprLet::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
DTRACE_EVAL_IN(state, let__in);
|
||||
|
||||
/* Create a new environment that contains the attributes in this
|
||||
`let'. */
|
||||
Env & env2(state.allocEnv(attrs->attrs.size()));
|
||||
|
@ -1429,22 +1455,32 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
|
|||
env2.values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
|
||||
|
||||
body->eval(state, env2, v);
|
||||
|
||||
DTRACE_EVAL_OUT(let__out, exprId);
|
||||
}
|
||||
|
||||
|
||||
void ExprList::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
DTRACE_EVAL_IN(state, list__in);
|
||||
|
||||
state.mkList(v, elems.size());
|
||||
for (auto [n, v2] : enumerate(v.listItems()))
|
||||
const_cast<Value * &>(v2) = elems[n]->maybeThunk(state, env);
|
||||
|
||||
DTRACE_EVAL_OUT(list__out, exprId);
|
||||
}
|
||||
|
||||
|
||||
void ExprVar::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
DTRACE_EVAL_IN(state, var__in);
|
||||
|
||||
Value * v2 = state.lookupVar(&env, *this, false);
|
||||
state.forceValue(*v2, pos);
|
||||
v = *v2;
|
||||
|
||||
DTRACE_EVAL_OUT(var__out, exprId);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1469,10 +1505,13 @@ static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & a
|
|||
|
||||
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
DTRACE_EVAL_IN(state, select__in);
|
||||
|
||||
Value vTmp;
|
||||
PosIdx pos2;
|
||||
Value * vAttrs = &vTmp;
|
||||
|
||||
|
||||
e->eval(state, env, vTmp);
|
||||
|
||||
try {
|
||||
|
@ -1496,6 +1535,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
|||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||
{
|
||||
def->eval(state, env, v);
|
||||
DTRACE_EVAL_OUT(select_short__out, exprId);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
@ -1526,11 +1566,15 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
|||
}
|
||||
|
||||
v = *vAttrs;
|
||||
|
||||
DTRACE_EVAL_OUT(select__out, exprId);
|
||||
}
|
||||
|
||||
|
||||
void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
DTRACE_EVAL_IN(state, has_attr__in);
|
||||
|
||||
Value vTmp;
|
||||
Value * vAttrs = &vTmp;
|
||||
|
||||
|
@ -1544,6 +1588,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
|||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||
{
|
||||
v.mkBool(false);
|
||||
DTRACE_EVAL_OUT(has_attr_failed__out, exprId);
|
||||
return;
|
||||
} else {
|
||||
vAttrs = j->value;
|
||||
|
@ -1551,12 +1596,17 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
|||
}
|
||||
|
||||
v.mkBool(true);
|
||||
DTRACE_EVAL_OUT(has_attr__out, exprId);
|
||||
}
|
||||
|
||||
|
||||
void ExprLambda::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
DTRACE_EVAL_IN(state, lambda__in);
|
||||
|
||||
v.mkLambda(&env, this);
|
||||
|
||||
DTRACE_EVAL_OUT(lambda__out, exprId);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1748,14 +1798,23 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
|
||||
void ExprCall::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
Value vFun;
|
||||
fun->eval(state, env, vFun);
|
||||
DTRACE_EVAL_IN(state, call__in);
|
||||
|
||||
Value * vArgs[args.size()];
|
||||
for (size_t i = 0; i < args.size(); ++i)
|
||||
vArgs[i] = args[i]->maybeThunk(state, env);
|
||||
try {
|
||||
Value vFun;
|
||||
fun->eval(state, env, vFun);
|
||||
|
||||
state.callFunction(vFun, args.size(), vArgs, v, pos);
|
||||
Value * vArgs[args.size()];
|
||||
for (size_t i = 0; i < args.size(); ++i)
|
||||
vArgs[i] = args[i]->maybeThunk(state, env);
|
||||
|
||||
state.callFunction(vFun, args.size(), vArgs, v, pos);
|
||||
|
||||
DTRACE_EVAL_OUT(call__out, exprId);
|
||||
} catch (AssertionError &e) {
|
||||
DTRACE_EVAL_OUT(call_throwned__out, exprId);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1808,7 +1867,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
|||
Nix attempted to evaluate a function as a top level expression; in
|
||||
this case it must have its arguments supplied either by default
|
||||
values, or passed explicitly with '--arg' or '--argstr'. See
|
||||
https://nixos.org/manual/nix/stable/expressions/language-constructs.html#functions.)", symbols[i.name],
|
||||
https://nixos.org/manual/nix/stable/expressions/language-constructs.html#functions.)", symbols[i.name],
|
||||
*fun.lambda.env, *fun.lambda.fun);
|
||||
}
|
||||
}
|
||||
|
@ -1820,6 +1879,8 @@ https://nixos.org/manual/nix/stable/expressions/language-constructs.html#functio
|
|||
|
||||
void ExprWith::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
DTRACE_EVAL_IN(state, with__in);
|
||||
|
||||
Env & env2(state.allocEnv(1));
|
||||
env2.up = &env;
|
||||
env2.prevWith = prevWith;
|
||||
|
@ -1827,23 +1888,33 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v)
|
|||
env2.values[0] = (Value *) attrs;
|
||||
|
||||
body->eval(state, env2, v);
|
||||
|
||||
DTRACE_EVAL_OUT(with__out, exprId);
|
||||
}
|
||||
|
||||
|
||||
void ExprIf::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
DTRACE_EVAL_IN(state, if__in);
|
||||
|
||||
(state.evalBool(env, cond, pos) ? then : else_)->eval(state, env, v);
|
||||
|
||||
DTRACE_EVAL_OUT(if__out, exprId);
|
||||
}
|
||||
|
||||
|
||||
void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
DTRACE_EVAL_IN(state, assert__in)
|
||||
|
||||
if (!state.evalBool(env, cond, pos)) {
|
||||
std::ostringstream out;
|
||||
cond->show(state.symbols, out);
|
||||
state.throwAssertionError(pos, "assertion '%1%' failed", out.str(), env, *this);
|
||||
}
|
||||
body->eval(state, env, v);
|
||||
|
||||
DTRACE_EVAL_OUT(assert__out, exprId);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1889,14 +1960,24 @@ void ExprOpImpl::eval(EvalState & state, Env & env, Value & v)
|
|||
|
||||
void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
DTRACE_EVAL_IN(state, op_update__in);
|
||||
|
||||
Value v1, v2;
|
||||
state.evalAttrs(env, e1, v1);
|
||||
state.evalAttrs(env, e2, v2);
|
||||
|
||||
state.nrOpUpdates++;
|
||||
|
||||
if (v1.attrs->size() == 0) { v = v2; return; }
|
||||
if (v2.attrs->size() == 0) { v = v1; return; }
|
||||
if (v1.attrs->size() == 0) {
|
||||
v = v2;
|
||||
DTRACE_EVAL_OUT(op_update_empty1__out, exprId);
|
||||
return;
|
||||
}
|
||||
if (v2.attrs->size() == 0) {
|
||||
v = v1;
|
||||
DTRACE_EVAL_OUT(op_update_empty2__out, exprId);
|
||||
return;
|
||||
}
|
||||
|
||||
auto attrs = state.buildBindings(v1.attrs->size() + v2.attrs->size());
|
||||
|
||||
|
@ -1922,6 +2003,8 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
|||
v.mkAttrs(attrs.alreadySorted());
|
||||
|
||||
state.nrOpUpdateValuesCopied += v.attrs->size();
|
||||
|
||||
DTRACE_EVAL_OUT(op_update__out, exprId);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -131,6 +131,8 @@ public:
|
|||
bool debugStop;
|
||||
bool debugQuit;
|
||||
int trylevel;
|
||||
|
||||
long exprId;
|
||||
std::list<DebugTrace> debugTraces;
|
||||
std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs;
|
||||
const std::shared_ptr<const StaticEnv> getStaticEnv(const Expr & expr) const
|
||||
|
|
|
@ -871,7 +871,6 @@ static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Va
|
|||
attrs.alloc(state.sValue).mkBool(false);
|
||||
attrs.alloc("success").mkBool(false);
|
||||
}
|
||||
|
||||
// restore the debugRepl pointer if we saved it earlier.
|
||||
if (savedDebugRepl)
|
||||
state.debugRepl = savedDebugRepl;
|
||||
|
|
Loading…
Reference in New Issue