Compare commits
6 commits
df5159e7d3
...
62c1bd0247
Author | SHA1 | Date | |
---|---|---|---|
Félix Baylac-Jacqué | 62c1bd0247 | ||
Félix Baylac-Jacqué | 1fdab971b3 | ||
Félix Baylac-Jacqué | 9b35ebe945 | ||
Félix Baylac-Jacqué | 0914a36b9a | ||
089f584173 | |||
a2b7baa42f |
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -127,3 +127,5 @@ nix-rust/target
|
||||||
result
|
result
|
||||||
|
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
|
target/
|
12
contrib/ebpf-profiler/ebpf-trace.c
Normal file
12
contrib/ebpf-profiler/ebpf-trace.c
Normal file
|
@ -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);
|
104
contrib/ebpf-profiler/ebpf-trace.py
Executable file
104
contrib/ebpf-profiler/ebpf-trace.py
Executable file
|
@ -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()
|
87
contrib/ebpf-profiler/fold-trace.rs
Normal file
87
contrib/ebpf-profiler/fold-trace.rs
Normal file
|
@ -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
|
||||||
|
}
|
170
contrib/ebpf-profiler/test-rust/Cargo.lock
generated
Normal file
170
contrib/ebpf-profiler/test-rust/Cargo.lock
generated
Normal file
|
@ -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"
|
10
contrib/ebpf-profiler/test-rust/Cargo.toml
Normal file
10
contrib/ebpf-profiler/test-rust/Cargo.toml
Normal file
|
@ -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 = "*"
|
5
contrib/ebpf-profiler/test-rust/src/hello.c
Normal file
5
contrib/ebpf-profiler/test-rust/src/hello.c
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
int some_func(void *ctx){
|
||||||
|
bpf_trace_printk("Hello, World!\\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
166
contrib/ebpf-profiler/test-rust/src/main.rs
Normal file
166
contrib/ebpf-profiler/test-rust/src/main.rs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
12
contrib/ebpf-profiler/test-rust/src/usdt_profiler_intro.c
Normal file
12
contrib/ebpf-profiler/test-rust/src/usdt_profiler_intro.c
Normal file
|
@ -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);
|
|
@ -115,6 +115,7 @@
|
||||||
boost
|
boost
|
||||||
lowdown-nix
|
lowdown-nix
|
||||||
gtest
|
gtest
|
||||||
|
libsystemtap
|
||||||
]
|
]
|
||||||
++ lib.optionals stdenv.isLinux [libseccomp]
|
++ lib.optionals stdenv.isLinux [libseccomp]
|
||||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
||||||
|
|
|
@ -810,7 +810,7 @@ EOF
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_sudo "to load data for the first time in to the Nix Database" \
|
_sudo "to load data for the first time in to the Nix Database" \
|
||||||
"$NIX_INSTALLED_NIX/bin/nix-store" --load-db < ./.reginfo
|
HOME="$ROOT_HOME" "$NIX_INSTALLED_NIX/bin/nix-store" --load-db < ./.reginfo
|
||||||
|
|
||||||
echo " Just finished getting the nix database ready."
|
echo " Just finished getting the nix database ready."
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
|
#include <sys/sdt.h>
|
||||||
|
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
|
|
||||||
|
@ -35,6 +36,17 @@
|
||||||
|
|
||||||
#endif
|
#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 {
|
namespace nix {
|
||||||
|
|
||||||
static char * allocString(size_t size)
|
static char * allocString(size_t size)
|
||||||
|
@ -468,6 +480,7 @@ EvalState::EvalState(
|
||||||
, debugStop(false)
|
, debugStop(false)
|
||||||
, debugQuit(false)
|
, debugQuit(false)
|
||||||
, trylevel(0)
|
, trylevel(0)
|
||||||
|
, exprId(0)
|
||||||
, regexCache(makeRegexCache())
|
, regexCache(makeRegexCache())
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
, valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
|
, valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
|
||||||
|
@ -1269,7 +1282,14 @@ void EvalState::cacheFile(
|
||||||
|
|
||||||
void EvalState::eval(Expr * e, Value & v)
|
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);
|
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)
|
void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
|
DTRACE_EVAL_IN(state, attrs__in);
|
||||||
|
|
||||||
v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish());
|
v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish());
|
||||||
auto dynamicEnv = &env;
|
auto dynamicEnv = &env;
|
||||||
|
|
||||||
|
@ -1411,11 +1433,15 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
v.attrs->pos = pos;
|
v.attrs->pos = pos;
|
||||||
|
|
||||||
|
DTRACE_EVAL_OUT(attrs__out, exprId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprLet::eval(EvalState & state, Env & env, Value & v)
|
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
|
/* Create a new environment that contains the attributes in this
|
||||||
`let'. */
|
`let'. */
|
||||||
Env & env2(state.allocEnv(attrs->attrs.size()));
|
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);
|
env2.values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
|
||||||
|
|
||||||
body->eval(state, env2, v);
|
body->eval(state, env2, v);
|
||||||
|
|
||||||
|
DTRACE_EVAL_OUT(let__out, exprId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprList::eval(EvalState & state, Env & env, Value & v)
|
void ExprList::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
|
DTRACE_EVAL_IN(state, list__in);
|
||||||
|
|
||||||
state.mkList(v, elems.size());
|
state.mkList(v, elems.size());
|
||||||
for (auto [n, v2] : enumerate(v.listItems()))
|
for (auto [n, v2] : enumerate(v.listItems()))
|
||||||
const_cast<Value * &>(v2) = elems[n]->maybeThunk(state, env);
|
const_cast<Value * &>(v2) = elems[n]->maybeThunk(state, env);
|
||||||
|
|
||||||
|
DTRACE_EVAL_OUT(list__out, exprId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprVar::eval(EvalState & state, Env & env, Value & v)
|
void ExprVar::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
|
DTRACE_EVAL_IN(state, var__in);
|
||||||
|
|
||||||
Value * v2 = state.lookupVar(&env, *this, false);
|
Value * v2 = state.lookupVar(&env, *this, false);
|
||||||
state.forceValue(*v2, pos);
|
state.forceValue(*v2, pos);
|
||||||
v = *v2;
|
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)
|
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
|
DTRACE_EVAL_IN(state, select__in);
|
||||||
|
|
||||||
Value vTmp;
|
Value vTmp;
|
||||||
PosIdx pos2;
|
PosIdx pos2;
|
||||||
Value * vAttrs = &vTmp;
|
Value * vAttrs = &vTmp;
|
||||||
|
|
||||||
|
|
||||||
e->eval(state, env, vTmp);
|
e->eval(state, env, vTmp);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1496,6 +1535,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||||
{
|
{
|
||||||
def->eval(state, env, v);
|
def->eval(state, env, v);
|
||||||
|
DTRACE_EVAL_OUT(select_short__out, exprId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1526,11 +1566,15 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
v = *vAttrs;
|
v = *vAttrs;
|
||||||
|
|
||||||
|
DTRACE_EVAL_OUT(select__out, exprId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
|
DTRACE_EVAL_IN(state, has_attr__in);
|
||||||
|
|
||||||
Value vTmp;
|
Value vTmp;
|
||||||
Value * vAttrs = &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())
|
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||||
{
|
{
|
||||||
v.mkBool(false);
|
v.mkBool(false);
|
||||||
|
DTRACE_EVAL_OUT(has_attr_failed__out, exprId);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
vAttrs = j->value;
|
vAttrs = j->value;
|
||||||
|
@ -1551,12 +1596,17 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
v.mkBool(true);
|
v.mkBool(true);
|
||||||
|
DTRACE_EVAL_OUT(has_attr__out, exprId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprLambda::eval(EvalState & state, Env & env, Value & v)
|
void ExprLambda::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
|
DTRACE_EVAL_IN(state, lambda__in);
|
||||||
|
|
||||||
v.mkLambda(&env, this);
|
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)
|
void ExprCall::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value vFun;
|
DTRACE_EVAL_IN(state, call__in);
|
||||||
fun->eval(state, env, vFun);
|
|
||||||
|
|
||||||
Value * vArgs[args.size()];
|
try {
|
||||||
for (size_t i = 0; i < args.size(); ++i)
|
Value vFun;
|
||||||
vArgs[i] = args[i]->maybeThunk(state, env);
|
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
|
Nix attempted to evaluate a function as a top level expression; in
|
||||||
this case it must have its arguments supplied either by default
|
this case it must have its arguments supplied either by default
|
||||||
values, or passed explicitly with '--arg' or '--argstr'. See
|
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);
|
*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)
|
void ExprWith::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
|
DTRACE_EVAL_IN(state, with__in);
|
||||||
|
|
||||||
Env & env2(state.allocEnv(1));
|
Env & env2(state.allocEnv(1));
|
||||||
env2.up = &env;
|
env2.up = &env;
|
||||||
env2.prevWith = prevWith;
|
env2.prevWith = prevWith;
|
||||||
|
@ -1827,23 +1888,33 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v)
|
||||||
env2.values[0] = (Value *) attrs;
|
env2.values[0] = (Value *) attrs;
|
||||||
|
|
||||||
body->eval(state, env2, v);
|
body->eval(state, env2, v);
|
||||||
|
|
||||||
|
DTRACE_EVAL_OUT(with__out, exprId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprIf::eval(EvalState & state, Env & env, Value & v)
|
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);
|
(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)
|
void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
|
DTRACE_EVAL_IN(state, assert__in)
|
||||||
|
|
||||||
if (!state.evalBool(env, cond, pos)) {
|
if (!state.evalBool(env, cond, pos)) {
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
cond->show(state.symbols, out);
|
cond->show(state.symbols, out);
|
||||||
state.throwAssertionError(pos, "assertion '%1%' failed", out.str(), env, *this);
|
state.throwAssertionError(pos, "assertion '%1%' failed", out.str(), env, *this);
|
||||||
}
|
}
|
||||||
body->eval(state, env, v);
|
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)
|
void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
|
DTRACE_EVAL_IN(state, op_update__in);
|
||||||
|
|
||||||
Value v1, v2;
|
Value v1, v2;
|
||||||
state.evalAttrs(env, e1, v1);
|
state.evalAttrs(env, e1, v1);
|
||||||
state.evalAttrs(env, e2, v2);
|
state.evalAttrs(env, e2, v2);
|
||||||
|
|
||||||
state.nrOpUpdates++;
|
state.nrOpUpdates++;
|
||||||
|
|
||||||
if (v1.attrs->size() == 0) { v = v2; return; }
|
if (v1.attrs->size() == 0) {
|
||||||
if (v2.attrs->size() == 0) { v = v1; return; }
|
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());
|
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());
|
v.mkAttrs(attrs.alreadySorted());
|
||||||
|
|
||||||
state.nrOpUpdateValuesCopied += v.attrs->size();
|
state.nrOpUpdateValuesCopied += v.attrs->size();
|
||||||
|
|
||||||
|
DTRACE_EVAL_OUT(op_update__out, exprId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -131,6 +131,8 @@ public:
|
||||||
bool debugStop;
|
bool debugStop;
|
||||||
bool debugQuit;
|
bool debugQuit;
|
||||||
int trylevel;
|
int trylevel;
|
||||||
|
|
||||||
|
long exprId;
|
||||||
std::list<DebugTrace> debugTraces;
|
std::list<DebugTrace> debugTraces;
|
||||||
std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs;
|
std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs;
|
||||||
const std::shared_ptr<const StaticEnv> getStaticEnv(const Expr & expr) const
|
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(state.sValue).mkBool(false);
|
||||||
attrs.alloc("success").mkBool(false);
|
attrs.alloc("success").mkBool(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore the debugRepl pointer if we saved it earlier.
|
// restore the debugRepl pointer if we saved it earlier.
|
||||||
if (savedDebugRepl)
|
if (savedDebugRepl)
|
||||||
state.debugRepl = savedDebugRepl;
|
state.debugRepl = savedDebugRepl;
|
||||||
|
|
Loading…
Reference in a new issue