Compare commits

...

5 Commits

Author SHA1 Message Date
Félix Baylac-Jacqué e8454af75b
wip - add rust-related missing files 2022-10-16 00:22:29 +02:00
Félix Baylac-Jacqué 62c1bd0247
wip 2022-10-15 22:20:24 +02:00
Félix Baylac-Jacqué 1fdab971b3
wip 2022-10-15 20:29:17 +02:00
Félix Baylac-Jacqué 9b35ebe945 wip 2 2022-10-05 18:12:20 +02:00
Félix Baylac-Jacqué 0914a36b9a wip 2022-09-10 15:59:17 +02:00
15 changed files with 671 additions and 10 deletions

2
.gitignore vendored
View File

@ -127,3 +127,5 @@ nix-rust/target
result
.vscode/
target/

View 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);

View 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()

View 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
View 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"

View 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 = "*"

View File

@ -0,0 +1,6 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
nativeBuildInputs = [ pkgs.rustc pkgs.cargo ] ++ pkgs.bcc.nativeBuildInputs ;
buildInputs = [ pkgs.bcc ];
}

View File

@ -0,0 +1,5 @@
int some_func(void *ctx){
bpf_trace_printk("Hello, World!\\n");
return 0;
}

View 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);
}
}

View 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);

View File

@ -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"

View File

@ -115,6 +115,7 @@
boost
lowdown-nix
gtest
libsystemtap
]
++ lib.optionals stdenv.isLinux [libseccomp]
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium

View File

@ -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);
}

View File

@ -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

View File

@ -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;