Compare commits

...

10 Commits

Author SHA1 Message Date
Félix Baylac Jacqué 9c3d91e331 Generate tracy trace from chrome profile 2024-02-06 13:57:43 +01:00
Andreas Rammhold 08e875fb33 Hack some JSON chrome shit 2022-10-21 16:58:36 +02:00
Andreas Rammhold 080bb5e304 Add tracing to primops 2022-10-21 01:35:42 +02:00
Andreas Rammhold b536c106a2 fixup! Add initial tracing prototype 2022-10-21 00:44:31 +02:00
Andreas Rammhold 1ff5eaf97c fixup! Add initial tracing prototype 2022-10-21 00:43:40 +02:00
Andreas Rammhold 543f3b0c80 fixup! Add initial tracing prototype 2022-10-20 21:22:46 +02:00
Andreas Rammhold 6359116c7b fixup! Add initial tracing prototype 2022-10-20 20:35:29 +02:00
Andreas Rammhold 999a0f6168 fixup! Add initial tracing prototype 2022-10-20 13:34:19 +02:00
Andreas Rammhold d9a4fd3266 Add initial tracing prototype 2022-10-20 12:10:46 +02:00
Andreas Rammhold 0370c44d21 Handle blackhole types while printing and don't shit the pants 2022-10-20 11:48:00 +02:00
9 changed files with 495 additions and 6 deletions

88
contrib/fold-trace.rs Normal file
View File

@ -0,0 +1,88 @@
use std::io;
use std::io::BufRead;
#[derive (PartialEq, Debug)]
enum Direction {
In,
Out
}
#[derive (PartialEq, Debug)]
struct Event {
ts: u128,
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_direction = match elems[1] {
"in" => Direction::In,
"out" => Direction::Out,
x => panic!("Unknown probe direction {}", x)
};
let probe_name = elems[4].to_string();
let filename = String::from(elems[3]);
let event = Event {
ts: elems[0].parse().expect(format!("Cannot parse timestamp for line {}", line_nb).as_str()),
id: elems[2].parse().expect(format!("Cannot parse probe id for line {}", line_nb).as_str()),
line: line_nb.clone(),
probe_name,
probe_direction,
line_col: String::from(elems[5]),
filename
};
if event.probe_direction == Direction::In {
stack.push(event);
} else {
println!("{}",event.id);
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
}

23
profile.sh Executable file
View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euo pipefail
nix-shell --run "make -j '$(nproc --all)'"
workspace=$(mktemp -d)
function cleanup {
rm -rf "${workspace}"
}
trap cleanup EXIT
chromejson="${workspace}"/out.json
tracyfile="${workspace}"/out.tracy
echo "[+] Instantiating derivation"
NIX_SHOW_TRACE=1 ./outputs/out/bin/nix-instantiate $@ > "${chromejson}"
echo "[+] Converting chrome profile to tracy profile"
nix-shell -p tracy --run "import-chrome '${chromejson}' '${tracyfile}'"
nix-shell -p tracy --run "tracy '${tracyfile}'"

View File

@ -9,6 +9,7 @@
#include "filetransfer.hh" #include "filetransfer.hh"
#include "json.hh" #include "json.hh"
#include "function-trace.hh" #include "function-trace.hh"
#include "tracing.hh"
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
@ -19,6 +20,7 @@
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <functional> #include <functional>
#include <deque>
#include <sys/resource.h> #include <sys/resource.h>
@ -37,6 +39,8 @@
namespace nix { namespace nix {
static char * allocString(size_t size) static char * allocString(size_t size)
{ {
char * t; char * t;
@ -175,6 +179,8 @@ void Value::print(const SymbolTable & symbols, std::ostream & str,
case tFloat: case tFloat:
str << fpoint; str << fpoint;
break; break;
case tBlackhole:
str << "<BLACKHOLE - currently evaluating this attribute>";
default: default:
abort(); abort();
} }
@ -479,6 +485,10 @@ EvalState::EvalState(
, baseEnv(allocEnv(128)) , baseEnv(allocEnv(128))
, staticBaseEnv{std::make_shared<StaticEnv>(false, nullptr)} , staticBaseEnv{std::make_shared<StaticEnv>(false, nullptr)}
{ {
bool showTrace = getEnv("NIX_SHOW_TRACE").value_or("0") != "0";
if (showTrace) {
tracingBuffer = std::make_unique<TracingBufferT>();
}
countCalls = getEnv("NIX_COUNT_CALLS").value_or("0") != "0"; countCalls = getEnv("NIX_COUNT_CALLS").value_or("0") != "0";
assert(gcInitialised); assert(gcInitialised);
@ -1265,17 +1275,85 @@ void EvalState::cacheFile(
fileEvalCache[resolvedPath] = v; fileEvalCache[resolvedPath] = v;
if (path != resolvedPath) fileEvalCache[path] = v; if (path != resolvedPath) fileEvalCache[path] = v;
} }
void EvalState::eval(Expr * e, Value & v) void EvalState::eval(Expr * e, Value & v)
{ {
NIX_TRACE_ES(*this, e)
e->eval(*this, baseEnv, v); e->eval(*this, baseEnv, v);
} }
void EvalState::printTraces() const {
bool showTrace = getEnv("NIX_SHOW_TRACE").value_or("0") != "0";
if (!tracingBuffer || !showTrace) {
return;
}
std::deque<TracingBufferT::TC::Entry*> stack;
std::cout << "[" << std::endl;
auto writeComma = false;
for (auto it = tracingBuffer->chunks.begin(); it != tracingBuffer->chunks.end(); it++) {
auto & chunk = *it;
for (size_t i = 0; i < chunk.pos; i++) {
auto * e = &chunk.data[i];
if(writeComma) {
std::cout << ",";
} else {
writeComma = true;
}
std::cout << "{\"name\": \"" << e->data.file << " " << e->data.line
<< " " << e->data.type << "\", \"cat\": \""
<< "empty"
<< "\", \"ph\": \"X\", \"pid\": 0, \"tid\": 0, \"ts\": "
<< e->ts_entry
<< ", \"dur\":" << e->ts_exit - e->ts_entry
<< "}"
<< std::endl;
//size_t n = 0;
//for (auto eit = stack.rbegin(); eit != stack.rend(); eit++) {
// auto element = *eit;
// if (element->ts_exit < e->ts_entry) {
// n++;
// std::cout << element->ts_exit << " out ";
// element->data.print(std::cout);
// std::cout << " " << std::endl;
// assert(!element->data.invalid);
// element->data.invalid = true;
// } else {
// break;
// }
//}
//while (n > 0) {
// stack.pop_back();
// n--;
//}
//std::cout << e->ts_entry << " in ";
//e->data.print(std::cout);
//std::cout << " " << std::endl;
//stack.push_back(e);
}
}
std::cout << "]";
for (auto eit = stack.rbegin(); eit != stack.rend(); eit++) {
auto element = *eit;
std::cout << element->ts_exit << " out ";
element->data.print(std::cout);
std::cout << " " << std::endl;
assert(!element->data.invalid);
element->data.invalid = true;
}
}
inline bool EvalState::evalBool(Env & env, Expr * e) inline bool EvalState::evalBool(Env & env, Expr * e)
{ {
Value v; Value v;
NIX_TRACE_TOP(*this,e)
e->eval(*this, env, v); e->eval(*this, env, v);
if (v.type() != nBool) if (v.type() != nBool)
throwTypeError(noPos, "value is %1% while a Boolean was expected", v, env, *e); throwTypeError(noPos, "value is %1% while a Boolean was expected", v, env, *e);
@ -1286,6 +1364,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e)
inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos) inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos)
{ {
Value v; Value v;
NIX_TRACE_TOP(*this, e)
e->eval(*this, env, v); e->eval(*this, env, v);
if (v.type() != nBool) if (v.type() != nBool)
throwTypeError(pos, "value is %1% while a Boolean was expected", v, env, *e); throwTypeError(pos, "value is %1% while a Boolean was expected", v, env, *e);
@ -1295,6 +1374,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos)
inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v) inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v)
{ {
NIX_TRACE_TOP(*this, e)
e->eval(*this, env, v); e->eval(*this, env, v);
if (v.type() != nAttrs) if (v.type() != nAttrs)
throwTypeError(noPos, "value is %1% while a set was expected", v, env, *e); throwTypeError(noPos, "value is %1% while a set was expected", v, env, *e);
@ -1309,29 +1389,34 @@ void Expr::eval(EvalState & state, Env & env, Value & v)
void ExprInt::eval(EvalState & state, Env & env, Value & v) void ExprInt::eval(EvalState & state, Env & env, Value & v)
{ {
v = this->v; NIX_TRACE_ES(state, this)
v = this->v;
} }
void ExprFloat::eval(EvalState & state, Env & env, Value & v) void ExprFloat::eval(EvalState & state, Env & env, Value & v)
{ {
v = this->v; NIX_TRACE_ES(state, this)
v = this->v;
} }
void ExprString::eval(EvalState & state, Env & env, Value & v) void ExprString::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
v = this->v; v = this->v;
} }
void ExprPath::eval(EvalState & state, Env & env, Value & v) void ExprPath::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
v = this->v; v = this->v;
} }
void ExprAttrs::eval(EvalState & state, Env & env, Value & v) void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish()); v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish());
auto dynamicEnv = &env; auto dynamicEnv = &env;
@ -1416,6 +1501,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
void ExprLet::eval(EvalState & state, Env & env, Value & v) void ExprLet::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
/* 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()));
@ -1434,6 +1520,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
void ExprList::eval(EvalState & state, Env & env, Value & v) void ExprList::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
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);
@ -1442,6 +1529,7 @@ void ExprList::eval(EvalState & state, Env & env, Value & v)
void ExprVar::eval(EvalState & state, Env & env, Value & v) void ExprVar::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
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;
@ -1469,6 +1557,8 @@ 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)
{ {
NIX_TRACE_ES(state, this)
Value vTmp; Value vTmp;
PosIdx pos2; PosIdx pos2;
Value * vAttrs = &vTmp; Value * vAttrs = &vTmp;
@ -1534,6 +1624,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
Value vTmp; Value vTmp;
Value * vAttrs = &vTmp; Value * vAttrs = &vTmp;
NIX_TRACE_ES(state, this)
e->eval(state, env, vTmp); e->eval(state, env, vTmp);
for (auto & i : attrPath) { for (auto & i : attrPath) {
@ -1556,6 +1647,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
void ExprLambda::eval(EvalState & state, Env & env, Value & v) void ExprLambda::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
v.mkLambda(&env, this); v.mkLambda(&env, this);
} }
@ -1749,6 +1841,7 @@ 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; Value vFun;
NIX_TRACE_ES(state, this)
fun->eval(state, env, vFun); fun->eval(state, env, vFun);
Value * vArgs[args.size()]; Value * vArgs[args.size()];
@ -1808,7 +1901,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 +1913,7 @@ 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)
{ {
NIX_TRACE_ES(state, this)
Env & env2(state.allocEnv(1)); Env & env2(state.allocEnv(1));
env2.up = &env; env2.up = &env;
env2.prevWith = prevWith; env2.prevWith = prevWith;
@ -1832,12 +1926,14 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v)
void ExprIf::eval(EvalState & state, Env & env, Value & v) void ExprIf::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
(state.evalBool(env, cond, pos) ? then : else_)->eval(state, env, v); (state.evalBool(env, cond, pos) ? then : else_)->eval(state, env, v);
} }
void ExprAssert::eval(EvalState & state, Env & env, Value & v) void ExprAssert::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
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);
@ -1849,12 +1945,14 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
void ExprOpNot::eval(EvalState & state, Env & env, Value & v) void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
v.mkBool(!state.evalBool(env, e)); v.mkBool(!state.evalBool(env, e));
} }
void ExprOpEq::eval(EvalState & state, Env & env, Value & v) void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
Value v1; e1->eval(state, env, v1); Value v1; e1->eval(state, env, v1);
Value v2; e2->eval(state, env, v2); Value v2; e2->eval(state, env, v2);
v.mkBool(state.eqValues(v1, v2)); v.mkBool(state.eqValues(v1, v2));
@ -1863,6 +1961,7 @@ void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
void ExprOpNEq::eval(EvalState & state, Env & env, Value & v) void ExprOpNEq::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
Value v1; e1->eval(state, env, v1); Value v1; e1->eval(state, env, v1);
Value v2; e2->eval(state, env, v2); Value v2; e2->eval(state, env, v2);
v.mkBool(!state.eqValues(v1, v2)); v.mkBool(!state.eqValues(v1, v2));
@ -1871,18 +1970,21 @@ void ExprOpNEq::eval(EvalState & state, Env & env, Value & v)
void ExprOpAnd::eval(EvalState & state, Env & env, Value & v) void ExprOpAnd::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
v.mkBool(state.evalBool(env, e1, pos) && state.evalBool(env, e2, pos)); v.mkBool(state.evalBool(env, e1, pos) && state.evalBool(env, e2, pos));
} }
void ExprOpOr::eval(EvalState & state, Env & env, Value & v) void ExprOpOr::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
v.mkBool(state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos)); v.mkBool(state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos));
} }
void ExprOpImpl::eval(EvalState & state, Env & env, Value & v) void ExprOpImpl::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
v.mkBool(!state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos)); v.mkBool(!state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos));
} }
@ -1890,6 +1992,7 @@ 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)
{ {
Value v1, v2; Value v1, v2;
NIX_TRACE_ES(state, this)
state.evalAttrs(env, e1, v1); state.evalAttrs(env, e1, v1);
state.evalAttrs(env, e2, v2); state.evalAttrs(env, e2, v2);
@ -1927,6 +2030,7 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
Value v1; e1->eval(state, env, v1); Value v1; e1->eval(state, env, v1);
Value v2; e2->eval(state, env, v2); Value v2; e2->eval(state, env, v2);
Value * lists[2] = { &v1, &v2 }; Value * lists[2] = { &v1, &v2 };
@ -1965,6 +2069,7 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
PathSet context; PathSet context;
std::vector<BackedStringView> s; std::vector<BackedStringView> s;
size_t sSize = 0; size_t sSize = 0;
@ -2054,6 +2159,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
void ExprPos::eval(EvalState & state, Env & env, Value & v) void ExprPos::eval(EvalState & state, Env & env, Value & v)
{ {
NIX_TRACE_ES(state, this)
state.mkPos(v, pos); state.mkPos(v, pos);
} }

View File

@ -7,6 +7,7 @@
#include "symbol-table.hh" #include "symbol-table.hh"
#include "config.hh" #include "config.hh"
#include "experimental-features.hh" #include "experimental-features.hh"
#include "tracing.hh"
#include <map> #include <map>
#include <optional> #include <optional>
@ -92,6 +93,7 @@ class EvalState : public std::enable_shared_from_this<EvalState>
public: public:
SymbolTable symbols; SymbolTable symbols;
PosTable positions; PosTable positions;
std::unique_ptr<TracingBufferT> tracingBuffer;
static inline std::string derivationNixPath = "//builtin/derivation.nix"; static inline std::string derivationNixPath = "//builtin/derivation.nix";
@ -126,6 +128,8 @@ public:
RootValue vCallFlake = nullptr; RootValue vCallFlake = nullptr;
RootValue vImportedDrvToDerivation = nullptr; RootValue vImportedDrvToDerivation = nullptr;
void printTraces() const;
/* Debugger */ /* Debugger */
void (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv); void (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
bool debugStop; bool debugStop;

View File

@ -147,6 +147,7 @@ struct Expr
virtual Value * maybeThunk(EvalState & state, Env & env); virtual Value * maybeThunk(EvalState & state, Env & env);
virtual void setName(Symbol name); virtual void setName(Symbol name);
virtual PosIdx getPos() const { return noPos; } virtual PosIdx getPos() const { return noPos; }
virtual const char* showExprType() const { return "undefined"; }
}; };
#define COMMON_METHODS \ #define COMMON_METHODS \
@ -160,6 +161,7 @@ struct ExprInt : Expr
Value v; Value v;
ExprInt(NixInt n) : n(n) { v.mkInt(n); }; ExprInt(NixInt n) : n(n) { v.mkInt(n); };
Value * maybeThunk(EvalState & state, Env & env) override; Value * maybeThunk(EvalState & state, Env & env) override;
const char* showExprType() const { return "int"; }
COMMON_METHODS COMMON_METHODS
}; };
@ -169,6 +171,7 @@ struct ExprFloat : Expr
Value v; Value v;
ExprFloat(NixFloat nf) : nf(nf) { v.mkFloat(nf); }; ExprFloat(NixFloat nf) : nf(nf) { v.mkFloat(nf); };
Value * maybeThunk(EvalState & state, Env & env) override; Value * maybeThunk(EvalState & state, Env & env) override;
const char* showExprType() const { return "float"; }
COMMON_METHODS COMMON_METHODS
}; };
@ -178,6 +181,7 @@ struct ExprString : Expr
Value v; Value v;
ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); }; ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); };
Value * maybeThunk(EvalState & state, Env & env) override; Value * maybeThunk(EvalState & state, Env & env) override;
const char* showExprType() const { return "string"; }
COMMON_METHODS COMMON_METHODS
}; };
@ -187,6 +191,7 @@ struct ExprPath : Expr
Value v; Value v;
ExprPath(std::string s) : s(std::move(s)) { v.mkPath(this->s.c_str()); }; ExprPath(std::string s) : s(std::move(s)) { v.mkPath(this->s.c_str()); };
Value * maybeThunk(EvalState & state, Env & env) override; Value * maybeThunk(EvalState & state, Env & env) override;
const char* showExprType() const { return "path"; }
COMMON_METHODS COMMON_METHODS
}; };
@ -215,6 +220,7 @@ struct ExprVar : Expr
ExprVar(const PosIdx & pos, Symbol name) : pos(pos), name(name) { }; ExprVar(const PosIdx & pos, Symbol name) : pos(pos), name(name) { };
Value * maybeThunk(EvalState & state, Env & env) override; Value * maybeThunk(EvalState & state, Env & env) override;
PosIdx getPos() const override { return pos; } PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "var"; }
COMMON_METHODS COMMON_METHODS
}; };
@ -226,6 +232,7 @@ struct ExprSelect : Expr
ExprSelect(const PosIdx & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { }; ExprSelect(const PosIdx & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { };
ExprSelect(const PosIdx & pos, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); }; ExprSelect(const PosIdx & pos, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
PosIdx getPos() const override { return pos; } PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "select"; }
COMMON_METHODS COMMON_METHODS
}; };
@ -235,6 +242,7 @@ struct ExprOpHasAttr : Expr
AttrPath attrPath; AttrPath attrPath;
ExprOpHasAttr(Expr * e, const AttrPath & attrPath) : e(e), attrPath(attrPath) { }; ExprOpHasAttr(Expr * e, const AttrPath & attrPath) : e(e), attrPath(attrPath) { };
PosIdx getPos() const override { return e->getPos(); } PosIdx getPos() const override { return e->getPos(); }
const char* showExprType() const { return "op_has_attr"; }
COMMON_METHODS COMMON_METHODS
}; };
@ -264,6 +272,7 @@ struct ExprAttrs : Expr
ExprAttrs(const PosIdx &pos) : recursive(false), pos(pos) { }; ExprAttrs(const PosIdx &pos) : recursive(false), pos(pos) { };
ExprAttrs() : recursive(false) { }; ExprAttrs() : recursive(false) { };
PosIdx getPos() const override { return pos; } PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "attrs"; }
COMMON_METHODS COMMON_METHODS
}; };
@ -271,6 +280,7 @@ struct ExprList : Expr
{ {
std::vector<Expr *> elems; std::vector<Expr *> elems;
ExprList() { }; ExprList() { };
const char* showExprType() const { return "list"; }
COMMON_METHODS COMMON_METHODS
PosIdx getPos() const override PosIdx getPos() const override
@ -330,6 +340,7 @@ struct ExprLambda : Expr
std::string showNamePos(const EvalState & state) const; std::string showNamePos(const EvalState & state) const;
inline bool hasFormals() const { return formals != nullptr; } inline bool hasFormals() const { return formals != nullptr; }
PosIdx getPos() const override { return pos; } PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "lambda"; }
COMMON_METHODS COMMON_METHODS
}; };
@ -342,6 +353,7 @@ struct ExprCall : Expr
: fun(fun), args(args), pos(pos) : fun(fun), args(args), pos(pos)
{ } { }
PosIdx getPos() const override { return pos; } PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "call"; }
COMMON_METHODS COMMON_METHODS
}; };
@ -350,6 +362,7 @@ struct ExprLet : Expr
ExprAttrs * attrs; ExprAttrs * attrs;
Expr * body; Expr * body;
ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { }; ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { };
const char* showExprType() const { return "let"; }
COMMON_METHODS COMMON_METHODS
}; };
@ -360,6 +373,7 @@ struct ExprWith : Expr
size_t prevWith; size_t prevWith;
ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { }; ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
PosIdx getPos() const override { return pos; } PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "with"; }
COMMON_METHODS COMMON_METHODS
}; };
@ -369,6 +383,7 @@ struct ExprIf : Expr
Expr * cond, * then, * else_; Expr * cond, * then, * else_;
ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { }; ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { };
PosIdx getPos() const override { return pos; } PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "if"; }
COMMON_METHODS COMMON_METHODS
}; };
@ -378,6 +393,7 @@ struct ExprAssert : Expr
Expr * cond, * body; Expr * cond, * body;
ExprAssert(const PosIdx & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { }; ExprAssert(const PosIdx & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
PosIdx getPos() const override { return pos; } PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "assert"; }
COMMON_METHODS COMMON_METHODS
}; };
@ -385,6 +401,7 @@ struct ExprOpNot : Expr
{ {
Expr * e; Expr * e;
ExprOpNot(Expr * e) : e(e) { }; ExprOpNot(Expr * e) : e(e) { };
const char* showExprType() const { return "op_not"; }
COMMON_METHODS COMMON_METHODS
}; };
@ -405,6 +422,7 @@ struct ExprOpNot : Expr
} \ } \
void eval(EvalState & state, Env & env, Value & v) override; \ void eval(EvalState & state, Env & env, Value & v) override; \
PosIdx getPos() const override { return pos; } \ PosIdx getPos() const override { return pos; } \
const char* showExprType() const { return #name; } \
}; };
MakeBinOp(ExprOpEq, "==") MakeBinOp(ExprOpEq, "==")
@ -423,6 +441,7 @@ struct ExprConcatStrings : Expr
ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector<std::pair<PosIdx, Expr *>> * es) ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector<std::pair<PosIdx, Expr *>> * es)
: pos(pos), forceString(forceString), es(es) { }; : pos(pos), forceString(forceString), es(es) { };
PosIdx getPos() const override { return pos; } PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "concat_strings"; }
COMMON_METHODS COMMON_METHODS
}; };
@ -431,6 +450,7 @@ struct ExprPos : Expr
PosIdx pos; PosIdx pos;
ExprPos(const PosIdx & pos) : pos(pos) { }; ExprPos(const PosIdx & pos) : pos(pos) { };
PosIdx getPos() const override { return pos; } PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "pos"; }
COMMON_METHODS COMMON_METHODS
}; };

View File

@ -11,6 +11,7 @@
#include "value-to-json.hh" #include "value-to-json.hh"
#include "value-to-xml.hh" #include "value-to-xml.hh"
#include "primops.hh" #include "primops.hh"
#include "tracing.hh"
#include <boost/container/small_vector.hpp> #include <boost/container/small_vector.hpp>
@ -101,6 +102,8 @@ static Path realisePath(EvalState & state, const PosIdx pos, Value & v, const Re
{ {
PathSet context; PathSet context;
NIX_TRACE(state, state.positions[pos], "realisePath");
auto path = [&]() auto path = [&]()
{ {
try { try {
@ -160,6 +163,7 @@ static void mkOutputString(
argument. */ argument. */
static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * vScope, Value & v) static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * vScope, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "import");
auto path = realisePath(state, pos, vPath); auto path = realisePath(state, pos, vPath);
// FIXME // FIXME
@ -313,6 +317,7 @@ extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v);
/* Load a ValueInitializer from a DSO and return whatever it initializes */ /* Load a ValueInitializer from a DSO and return whatever it initializes */
void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Value & v) void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "importNative");
auto path = realisePath(state, pos, *args[0]); auto path = realisePath(state, pos, *args[0]);
std::string sym(state.forceStringNoCtx(*args[1], pos)); std::string sym(state.forceStringNoCtx(*args[1], pos));
@ -340,6 +345,7 @@ void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Valu
/* Execute a program and parse its output */ /* Execute a program and parse its output */
void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v) void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "exec");
state.forceList(*args[0], pos); state.forceList(*args[0], pos);
auto elems = args[0]->listElems(); auto elems = args[0]->listElems();
auto count = args[0]->listSize(); auto count = args[0]->listSize();
@ -385,6 +391,7 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v)
/* Return a string representing the type of the expression. */ /* Return a string representing the type of the expression. */
static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "typeOf");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
std::string t; std::string t;
switch (args[0]->type()) { switch (args[0]->type()) {
@ -419,6 +426,7 @@ static RegisterPrimOp primop_typeOf({
/* Determine whether the argument is the null value. */ /* Determine whether the argument is the null value. */
static void prim_isNull(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_isNull(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "isNull");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nNull); v.mkBool(args[0]->type() == nNull);
} }
@ -439,6 +447,7 @@ static RegisterPrimOp primop_isNull({
/* Determine whether the argument is a function. */ /* Determine whether the argument is a function. */
static void prim_isFunction(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_isFunction(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "isFunction");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nFunction); v.mkBool(args[0]->type() == nFunction);
} }
@ -455,6 +464,7 @@ static RegisterPrimOp primop_isFunction({
/* Determine whether the argument is an integer. */ /* Determine whether the argument is an integer. */
static void prim_isInt(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_isInt(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "isInt");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nInt); v.mkBool(args[0]->type() == nInt);
} }
@ -471,6 +481,7 @@ static RegisterPrimOp primop_isInt({
/* Determine whether the argument is a float. */ /* Determine whether the argument is a float. */
static void prim_isFloat(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_isFloat(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "isFloat");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nFloat); v.mkBool(args[0]->type() == nFloat);
} }
@ -487,6 +498,7 @@ static RegisterPrimOp primop_isFloat({
/* Determine whether the argument is a string. */ /* Determine whether the argument is a string. */
static void prim_isString(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_isString(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "isString");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nString); v.mkBool(args[0]->type() == nString);
} }
@ -503,6 +515,7 @@ static RegisterPrimOp primop_isString({
/* Determine whether the argument is a Boolean. */ /* Determine whether the argument is a Boolean. */
static void prim_isBool(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_isBool(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "isBool");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nBool); v.mkBool(args[0]->type() == nBool);
} }
@ -519,6 +532,7 @@ static RegisterPrimOp primop_isBool({
/* Determine whether the argument is a path. */ /* Determine whether the argument is a path. */
static void prim_isPath(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_isPath(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "isPath");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nPath); v.mkBool(args[0]->type() == nPath);
} }
@ -619,6 +633,7 @@ static Bindings::iterator getAttr(
static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "genericClosure");
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
/* Get the start set. */ /* Get the start set. */
@ -733,6 +748,7 @@ static RegisterPrimOp primop_break({
)", )",
.fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "break");
if (state.debugRepl && !state.debugTraces.empty()) { if (state.debugRepl && !state.debugTraces.empty()) {
auto error = Error(ErrorInfo { auto error = Error(ErrorInfo {
.level = lvlInfo, .level = lvlInfo,
@ -766,6 +782,7 @@ static RegisterPrimOp primop_abort({
)", )",
.fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "abort");
PathSet context; PathSet context;
auto s = state.coerceToString(pos, *args[0], context).toOwned(); auto s = state.coerceToString(pos, *args[0], context).toOwned();
state.debugThrowLastTrace(Abort("evaluation aborted with the following error message: '%1%'", s)); state.debugThrowLastTrace(Abort("evaluation aborted with the following error message: '%1%'", s));
@ -784,6 +801,7 @@ static RegisterPrimOp primop_throw({
)", )",
.fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "throw");
PathSet context; PathSet context;
auto s = state.coerceToString(pos, *args[0], context).toOwned(); auto s = state.coerceToString(pos, *args[0], context).toOwned();
state.debugThrowLastTrace(ThrownError(s)); state.debugThrowLastTrace(ThrownError(s));
@ -792,6 +810,7 @@ static RegisterPrimOp primop_throw({
static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "addErrorContext");
try { try {
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
v = *args[1]; v = *args[1];
@ -810,6 +829,7 @@ static RegisterPrimOp primop_addErrorContext(RegisterPrimOp::Info {
static void prim_ceil(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_ceil(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "ceil");
auto value = state.forceFloat(*args[0], args[0]->determinePos(pos)); auto value = state.forceFloat(*args[0], args[0]->determinePos(pos));
v.mkInt(ceil(value)); v.mkInt(ceil(value));
} }
@ -829,6 +849,8 @@ static RegisterPrimOp primop_ceil({
static void prim_floor(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_floor(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "floor");
auto value = state.forceFloat(*args[0], args[0]->determinePos(pos)); auto value = state.forceFloat(*args[0], args[0]->determinePos(pos));
v.mkInt(floor(value)); v.mkInt(floor(value));
} }
@ -850,6 +872,7 @@ static RegisterPrimOp primop_floor({
* else => {success=false; value=false;} */ * else => {success=false; value=false;} */
static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "tryEval");
auto attrs = state.buildBindings(2); auto attrs = state.buildBindings(2);
/* increment state.trylevel, and decrement it when this function returns. */ /* increment state.trylevel, and decrement it when this function returns. */
@ -903,6 +926,7 @@ static RegisterPrimOp primop_tryEval({
/* Return an environment variable. Use with care. */ /* Return an environment variable. Use with care. */
static void prim_getEnv(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_getEnv(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "getEnv");
std::string name(state.forceStringNoCtx(*args[0], pos)); std::string name(state.forceStringNoCtx(*args[0], pos));
v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or("")); v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or(""));
} }
@ -927,6 +951,7 @@ static RegisterPrimOp primop_getEnv({
/* Evaluate the first argument, then return the second argument. */ /* Evaluate the first argument, then return the second argument. */
static void prim_seq(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_seq(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "seq");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
v = *args[1]; v = *args[1];
@ -946,6 +971,7 @@ static RegisterPrimOp primop_seq({
attrsets), then return the second argument. */ attrsets), then return the second argument. */
static void prim_deepSeq(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_deepSeq(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "deepSeq");
state.forceValueDeep(*args[0]); state.forceValueDeep(*args[0]);
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
v = *args[1]; v = *args[1];
@ -966,6 +992,7 @@ static RegisterPrimOp primop_deepSeq({
return the second expression. Useful for debugging. */ return the second expression. Useful for debugging. */
static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "trace");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
if (args[0]->type() == nString) if (args[0]->type() == nString)
printError("trace: %1%", args[0]->string.s); printError("trace: %1%", args[0]->string.s);
@ -992,6 +1019,7 @@ static RegisterPrimOp primop_trace({
*/ */
static void prim_second(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_second(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "second");
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
v = *args[1]; v = *args[1];
} }
@ -1010,6 +1038,7 @@ static void prim_second(EvalState & state, const PosIdx pos, Value * * args, Val
derivation. */ derivation. */
static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "derivationStrict");
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
/* Figure out the name first (for stack backtraces). */ /* Figure out the name first (for stack backtraces). */
@ -1368,6 +1397,7 @@ static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info {
out. */ out. */
static void prim_placeholder(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_placeholder(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "placeholder");
v.mkString(hashPlaceholder(state.forceStringNoCtx(*args[0], pos))); v.mkString(hashPlaceholder(state.forceStringNoCtx(*args[0], pos)));
} }
@ -1391,6 +1421,7 @@ static RegisterPrimOp primop_placeholder({
/* Convert the argument to a path. !!! obsolete? */ /* Convert the argument to a path. !!! obsolete? */
static void prim_toPath(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_toPath(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "toPath");
PathSet context; PathSet context;
Path path = state.coerceToPath(pos, *args[0], context); Path path = state.coerceToPath(pos, *args[0], context);
v.mkString(canonPath(path), context); v.mkString(canonPath(path), context);
@ -1416,6 +1447,7 @@ static RegisterPrimOp primop_toPath({
corner cases. */ corner cases. */
static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "storePath");
if (evalSettings.pureEval) if (evalSettings.pureEval)
state.debugThrowLastTrace(EvalError({ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("'%s' is not allowed in pure evaluation mode", "builtins.storePath"), .msg = hintfmt("'%s' is not allowed in pure evaluation mode", "builtins.storePath"),
@ -1460,6 +1492,7 @@ static RegisterPrimOp primop_storePath({
static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "pathExists");
/* We dont check the path right now, because we dont want to /* We dont check the path right now, because we dont want to
throw if the path isnt allowed, but just return false (and we throw if the path isnt allowed, but just return false (and we
cant just catch the exception here because we still want to cant just catch the exception here because we still want to
@ -1492,6 +1525,7 @@ static RegisterPrimOp primop_pathExists({
following the last slash. */ following the last slash. */
static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "baseNameOf");
PathSet context; PathSet context;
v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false)), context); v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false)), context);
} }
@ -1512,6 +1546,7 @@ static RegisterPrimOp primop_baseNameOf({
of the argument. */ of the argument. */
static void prim_dirOf(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_dirOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "dirOf");
PathSet context; PathSet context;
auto path = state.coerceToString(pos, *args[0], context, false, false); auto path = state.coerceToString(pos, *args[0], context, false, false);
auto dir = dirOf(*path); auto dir = dirOf(*path);
@ -1532,6 +1567,7 @@ static RegisterPrimOp primop_dirOf({
/* Return the contents of a file as a string. */ /* Return the contents of a file as a string. */
static void prim_readFile(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_readFile(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "readFile");
auto path = realisePath(state, pos, *args[0]); auto path = realisePath(state, pos, *args[0]);
auto s = readFile(path); auto s = readFile(path);
if (s.find((char) 0) != std::string::npos) if (s.find((char) 0) != std::string::npos)
@ -1560,6 +1596,7 @@ static RegisterPrimOp primop_readFile({
which are desugared to 'findFile __nixPath "x"'. */ which are desugared to 'findFile __nixPath "x"'. */
static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "findFile");
state.forceList(*args[0], pos); state.forceList(*args[0], pos);
SearchPath searchPath; SearchPath searchPath;
@ -1610,6 +1647,7 @@ static RegisterPrimOp primop_findFile(RegisterPrimOp::Info {
/* Return the cryptographic hash of a file in base-16. */ /* Return the cryptographic hash of a file in base-16. */
static void prim_hashFile(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_hashFile(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "hashFile");
auto type = state.forceStringNoCtx(*args[0], pos); auto type = state.forceStringNoCtx(*args[0], pos);
std::optional<HashType> ht = parseHashType(type); std::optional<HashType> ht = parseHashType(type);
if (!ht) if (!ht)
@ -1637,6 +1675,7 @@ static RegisterPrimOp primop_hashFile({
/* Read a directory (without . or ..) */ /* Read a directory (without . or ..) */
static void prim_readDir(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_readDir(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "readDir");
auto path = realisePath(state, pos, *args[0]); auto path = realisePath(state, pos, *args[0]);
DirEntries entries = readDirectory(path); DirEntries entries = readDirectory(path);
@ -1686,6 +1725,7 @@ static RegisterPrimOp primop_readDir({
be sensibly or completely represented (e.g., functions). */ be sensibly or completely represented (e.g., functions). */
static void prim_toXML(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_toXML(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "toXML");
std::ostringstream out; std::ostringstream out;
PathSet context; PathSet context;
printValueAsXML(state, true, false, *args[0], out, context, pos); printValueAsXML(state, true, false, *args[0], out, context, pos);
@ -1794,6 +1834,7 @@ static RegisterPrimOp primop_toXML({
represented (e.g., functions). */ represented (e.g., functions). */
static void prim_toJSON(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_toJSON(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "toJSON");
std::ostringstream out; std::ostringstream out;
PathSet context; PathSet context;
printValueAsJSON(state, true, *args[0], pos, out, context); printValueAsJSON(state, true, *args[0], pos, out, context);
@ -1817,6 +1858,7 @@ static RegisterPrimOp primop_toJSON({
/* Parse a JSON string to a value. */ /* Parse a JSON string to a value. */
static void prim_fromJSON(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_fromJSON(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "fromJSON");
auto s = state.forceStringNoCtx(*args[0], pos); auto s = state.forceStringNoCtx(*args[0], pos);
try { try {
parseJSON(state, s, v); parseJSON(state, s, v);
@ -1845,6 +1887,7 @@ static RegisterPrimOp primop_fromJSON({
as an input by derivations. */ as an input by derivations. */
static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "toFile");
PathSet context; PathSet context;
std::string name(state.forceStringNoCtx(*args[0], pos)); std::string name(state.forceStringNoCtx(*args[0], pos));
std::string contents(state.forceString(*args[1], context, pos)); std::string contents(state.forceString(*args[1], context, pos));
@ -1964,6 +2007,7 @@ static void addPath(
Value & v, Value & v,
const PathSet & context) const PathSet & context)
{ {
NIX_TRACE(state, state.positions[pos], "addPath");
try { try {
// FIXME: handle CA derivation outputs (where path needs to // FIXME: handle CA derivation outputs (where path needs to
// be rewritten to the actual output). // be rewritten to the actual output).
@ -2030,6 +2074,7 @@ static void addPath(
static void prim_filterSource(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_filterSource(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "filterSource");
PathSet context; PathSet context;
Path path = state.coerceToPath(pos, *args[1], context); Path path = state.coerceToPath(pos, *args[1], context);
@ -2102,6 +2147,7 @@ static RegisterPrimOp primop_filterSource({
static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "path");
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
Path path; Path path;
std::string name; std::string name;
@ -2185,6 +2231,7 @@ static RegisterPrimOp primop_path({
strings. */ strings. */
static void prim_attrNames(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_attrNames(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "attrNames");
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
state.mkList(v, args[0]->attrs->size()); state.mkList(v, args[0]->attrs->size());
@ -2212,6 +2259,7 @@ static RegisterPrimOp primop_attrNames({
order as attrNames. */ order as attrNames. */
static void prim_attrValues(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_attrValues(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "attrValues");
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
state.mkList(v, args[0]->attrs->size()); state.mkList(v, args[0]->attrs->size());
@ -2244,6 +2292,7 @@ static RegisterPrimOp primop_attrValues({
/* Dynamic version of the `.' operator. */ /* Dynamic version of the `.' operator. */
void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v) void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "getAttr");
auto attr = state.forceStringNoCtx(*args[0], pos); auto attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos);
Bindings::iterator i = getAttr( Bindings::iterator i = getAttr(
@ -2274,6 +2323,7 @@ static RegisterPrimOp primop_getAttr({
/* Return position information of the specified attribute. */ /* Return position information of the specified attribute. */
static void prim_unsafeGetAttrPos(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_unsafeGetAttrPos(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "unsafeGetAttrPos");
auto attr = state.forceStringNoCtx(*args[0], pos); auto attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos);
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr)); Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
@ -2292,6 +2342,7 @@ static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info {
/* Dynamic version of the `?' operator. */ /* Dynamic version of the `?' operator. */
static void prim_hasAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_hasAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "hasAttr");
auto attr = state.forceStringNoCtx(*args[0], pos); auto attr = state.forceStringNoCtx(*args[0], pos);
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos);
v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end()); v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end());
@ -2311,6 +2362,7 @@ static RegisterPrimOp primop_hasAttr({
/* Determine whether the argument is a set. */ /* Determine whether the argument is a set. */
static void prim_isAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_isAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "isAttrs");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nAttrs); v.mkBool(args[0]->type() == nAttrs);
} }
@ -2326,6 +2378,7 @@ static RegisterPrimOp primop_isAttrs({
static void prim_removeAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_removeAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "removeAttrs");
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
@ -2374,6 +2427,7 @@ static RegisterPrimOp primop_removeAttrs({
name, the first takes precedence. */ name, the first takes precedence. */
static void prim_listToAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_listToAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "listToAttrs");
state.forceList(*args[0], pos); state.forceList(*args[0], pos);
auto attrs = state.buildBindings(args[0]->listSize()); auto attrs = state.buildBindings(args[0]->listSize());
@ -2436,6 +2490,7 @@ static RegisterPrimOp primop_listToAttrs({
static void prim_intersectAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_intersectAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "intersectAttrs");
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos);
@ -2462,6 +2517,7 @@ static RegisterPrimOp primop_intersectAttrs({
static void prim_catAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_catAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "catAttrs");
auto attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos)); auto attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos));
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
@ -2499,6 +2555,7 @@ static RegisterPrimOp primop_catAttrs({
static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "functionArgs");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
if (args[0]->isPrimOpApp() || args[0]->isPrimOp()) { if (args[0]->isPrimOpApp() || args[0]->isPrimOp()) {
v.mkAttrs(&state.emptyBindings); v.mkAttrs(&state.emptyBindings);
@ -2542,6 +2599,7 @@ static RegisterPrimOp primop_functionArgs({
/* */ /* */
static void prim_mapAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_mapAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "mapAttrs");
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos);
auto attrs = state.buildBindings(args[1]->attrs->size()); auto attrs = state.buildBindings(args[1]->attrs->size());
@ -2574,6 +2632,7 @@ static RegisterPrimOp primop_mapAttrs({
static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "zipAttrsWith");
// we will first count how many values are present for each given key. // we will first count how many values are present for each given key.
// we then allocate a single attrset and pre-populate it with lists of // we then allocate a single attrset and pre-populate it with lists of
// appropriate sizes, stash the pointers to the list elements of each, // appropriate sizes, stash the pointers to the list elements of each,
@ -2666,6 +2725,7 @@ static RegisterPrimOp primop_zipAttrsWith({
/* Determine whether the argument is a list. */ /* Determine whether the argument is a list. */
static void prim_isList(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_isList(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "isList");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nList); v.mkBool(args[0]->type() == nList);
} }
@ -2681,6 +2741,7 @@ static RegisterPrimOp primop_isList({
static void elemAt(EvalState & state, const PosIdx pos, Value & list, int n, Value & v) static void elemAt(EvalState & state, const PosIdx pos, Value & list, int n, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "elemAt");
state.forceList(list, pos); state.forceList(list, pos);
if (n < 0 || (unsigned int) n >= list.listSize()) if (n < 0 || (unsigned int) n >= list.listSize())
state.debugThrowLastTrace(Error({ state.debugThrowLastTrace(Error({
@ -2694,6 +2755,7 @@ static void elemAt(EvalState & state, const PosIdx pos, Value & list, int n, Val
/* Return the n-1'th element of a list. */ /* Return the n-1'th element of a list. */
static void prim_elemAt(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_elemAt(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "elemAt");
elemAt(state, pos, *args[0], state.forceInt(*args[1], pos), v); elemAt(state, pos, *args[0], state.forceInt(*args[1], pos), v);
} }
@ -2710,6 +2772,7 @@ static RegisterPrimOp primop_elemAt({
/* Return the first element of a list. */ /* Return the first element of a list. */
static void prim_head(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_head(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "head");
elemAt(state, pos, *args[0], 0, v); elemAt(state, pos, *args[0], 0, v);
} }
@ -2729,6 +2792,7 @@ static RegisterPrimOp primop_head({
don't want to use it! */ don't want to use it! */
static void prim_tail(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_tail(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "tail");
state.forceList(*args[0], pos); state.forceList(*args[0], pos);
if (args[0]->listSize() == 0) if (args[0]->listSize() == 0)
state.debugThrowLastTrace(Error({ state.debugThrowLastTrace(Error({
@ -2760,6 +2824,7 @@ static RegisterPrimOp primop_tail({
/* Apply a function to every element of a list. */ /* Apply a function to every element of a list. */
static void prim_map(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_map(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "map");
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
state.mkList(v, args[1]->listSize()); state.mkList(v, args[1]->listSize());
@ -2790,6 +2855,7 @@ static RegisterPrimOp primop_map({
returns true. */ returns true. */
static void prim_filter(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_filter(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "filter");
state.forceFunction(*args[0], pos); state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
@ -2828,6 +2894,7 @@ static RegisterPrimOp primop_filter({
/* Return true if a list contains a given element. */ /* Return true if a list contains a given element. */
static void prim_elem(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_elem(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "elem");
bool res = false; bool res = false;
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
for (auto elem : args[1]->listItems()) for (auto elem : args[1]->listItems())
@ -2851,6 +2918,7 @@ static RegisterPrimOp primop_elem({
/* Concatenate a list of lists. */ /* Concatenate a list of lists. */
static void prim_concatLists(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_concatLists(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "concatLists");
state.forceList(*args[0], pos); state.forceList(*args[0], pos);
state.concatLists(v, args[0]->listSize(), args[0]->listElems(), pos); state.concatLists(v, args[0]->listSize(), args[0]->listElems(), pos);
} }
@ -2867,6 +2935,7 @@ static RegisterPrimOp primop_concatLists({
/* Return the length of a list. This is an O(1) time operation. */ /* Return the length of a list. This is an O(1) time operation. */
static void prim_length(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_length(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "length");
state.forceList(*args[0], pos); state.forceList(*args[0], pos);
v.mkInt(args[0]->listSize()); v.mkInt(args[0]->listSize());
} }
@ -2884,6 +2953,7 @@ static RegisterPrimOp primop_length({
right. The operator is applied strictly. */ right. The operator is applied strictly. */
static void prim_foldlStrict(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_foldlStrict(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "foldlStrict");
state.forceFunction(*args[0], pos); state.forceFunction(*args[0], pos);
state.forceList(*args[2], pos); state.forceList(*args[2], pos);
@ -2917,6 +2987,7 @@ static RegisterPrimOp primop_foldlStrict({
static void anyOrAll(bool any, EvalState & state, const PosIdx pos, Value * * args, Value & v) static void anyOrAll(bool any, EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "anyOrAll");
state.forceFunction(*args[0], pos); state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
@ -2936,6 +3007,7 @@ static void anyOrAll(bool any, EvalState & state, const PosIdx pos, Value * * ar
static void prim_any(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_any(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "any");
anyOrAll(true, state, pos, args, v); anyOrAll(true, state, pos, args, v);
} }
@ -2951,6 +3023,7 @@ static RegisterPrimOp primop_any({
static void prim_all(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_all(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "all");
anyOrAll(false, state, pos, args, v); anyOrAll(false, state, pos, args, v);
} }
@ -2966,6 +3039,7 @@ static RegisterPrimOp primop_all({
static void prim_genList(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_genList(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "genList");
auto len = state.forceInt(*args[1], pos); auto len = state.forceInt(*args[1], pos);
if (len < 0) if (len < 0)
@ -3004,6 +3078,7 @@ static void prim_lessThan(EvalState & state, const PosIdx pos, Value * * args, V
static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "sort");
state.forceFunction(*args[0], pos); state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
@ -3055,6 +3130,7 @@ static RegisterPrimOp primop_sort({
static void prim_partition(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_partition(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "partition");
state.forceFunction(*args[0], pos); state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
@ -3115,6 +3191,7 @@ static RegisterPrimOp primop_partition({
static void prim_groupBy(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_groupBy(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "groupBy");
state.forceFunction(*args[0], pos); state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
@ -3167,6 +3244,7 @@ static RegisterPrimOp primop_groupBy({
static void prim_concatMap(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_concatMap(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "concatMap");
state.forceFunction(*args[0], pos); state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
auto nrLists = args[1]->listSize(); auto nrLists = args[1]->listSize();
@ -3214,6 +3292,7 @@ static RegisterPrimOp primop_concatMap({
static void prim_add(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_add(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "add");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
if (args[0]->type() == nFloat || args[1]->type() == nFloat) if (args[0]->type() == nFloat || args[1]->type() == nFloat)
@ -3233,6 +3312,7 @@ static RegisterPrimOp primop_add({
static void prim_sub(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_sub(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "sub");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
if (args[0]->type() == nFloat || args[1]->type() == nFloat) if (args[0]->type() == nFloat || args[1]->type() == nFloat)
@ -3252,6 +3332,7 @@ static RegisterPrimOp primop_sub({
static void prim_mul(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_mul(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "mul");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
if (args[0]->type() == nFloat || args[1]->type() == nFloat) if (args[0]->type() == nFloat || args[1]->type() == nFloat)
@ -3271,6 +3352,7 @@ static RegisterPrimOp primop_mul({
static void prim_div(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_div(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "div");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
@ -3308,6 +3390,7 @@ static RegisterPrimOp primop_div({
static void prim_bitAnd(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_bitAnd(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "bitAnd");
v.mkInt(state.forceInt(*args[0], pos) & state.forceInt(*args[1], pos)); v.mkInt(state.forceInt(*args[0], pos) & state.forceInt(*args[1], pos));
} }
@ -3322,6 +3405,7 @@ static RegisterPrimOp primop_bitAnd({
static void prim_bitOr(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_bitOr(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "bitOr");
v.mkInt(state.forceInt(*args[0], pos) | state.forceInt(*args[1], pos)); v.mkInt(state.forceInt(*args[0], pos) | state.forceInt(*args[1], pos));
} }
@ -3336,6 +3420,7 @@ static RegisterPrimOp primop_bitOr({
static void prim_bitXor(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_bitXor(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "bitXor");
v.mkInt(state.forceInt(*args[0], pos) ^ state.forceInt(*args[1], pos)); v.mkInt(state.forceInt(*args[0], pos) ^ state.forceInt(*args[1], pos));
} }
@ -3350,6 +3435,7 @@ static RegisterPrimOp primop_bitXor({
static void prim_lessThan(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_lessThan(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "lessThan");
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
CompareValues comp{state}; CompareValues comp{state};
@ -3378,6 +3464,7 @@ static RegisterPrimOp primop_lessThan({
`"/nix/store/whatever..."'. */ `"/nix/store/whatever..."'. */
static void prim_toString(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_toString(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "toString");
PathSet context; PathSet context;
auto s = state.coerceToString(pos, *args[0], context, true, false); auto s = state.coerceToString(pos, *args[0], context, true, false);
v.mkString(*s, context); v.mkString(*s, context);
@ -3413,6 +3500,7 @@ static RegisterPrimOp primop_toString({
non-negative. */ non-negative. */
static void prim_substring(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_substring(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "substring");
int start = state.forceInt(*args[0], pos); int start = state.forceInt(*args[0], pos);
int len = state.forceInt(*args[1], pos); int len = state.forceInt(*args[1], pos);
PathSet context; PathSet context;
@ -3449,6 +3537,7 @@ static RegisterPrimOp primop_substring({
static void prim_stringLength(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_stringLength(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "stringLength");
PathSet context; PathSet context;
auto s = state.coerceToString(pos, *args[0], context); auto s = state.coerceToString(pos, *args[0], context);
v.mkInt(s->size()); v.mkInt(s->size());
@ -3467,6 +3556,7 @@ static RegisterPrimOp primop_stringLength({
/* Return the cryptographic hash of a string in base-16. */ /* Return the cryptographic hash of a string in base-16. */
static void prim_hashString(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_hashString(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "hashString");
auto type = state.forceStringNoCtx(*args[0], pos); auto type = state.forceStringNoCtx(*args[0], pos);
std::optional<HashType> ht = parseHashType(type); std::optional<HashType> ht = parseHashType(type);
if (!ht) if (!ht)
@ -3515,6 +3605,7 @@ std::shared_ptr<RegexCache> makeRegexCache()
void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v) void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "match");
auto re = state.forceStringNoCtx(*args[0], pos); auto re = state.forceStringNoCtx(*args[0], pos);
try { try {
@ -3595,6 +3686,7 @@ static RegisterPrimOp primop_match({
non-matching parts interleaved by the lists of the matching groups. */ non-matching parts interleaved by the lists of the matching groups. */
void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v) void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "split");
auto re = state.forceStringNoCtx(*args[0], pos); auto re = state.forceStringNoCtx(*args[0], pos);
try { try {
@ -3698,6 +3790,7 @@ static RegisterPrimOp primop_split({
static void prim_concatStringsSep(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_concatStringsSep(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "concatStringsSep");
PathSet context; PathSet context;
auto sep = state.forceString(*args[0], context, pos); auto sep = state.forceString(*args[0], context, pos);
@ -3728,6 +3821,7 @@ static RegisterPrimOp primop_concatStringsSep({
static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "replaceStrings");
state.forceList(*args[0], pos); state.forceList(*args[0], pos);
state.forceList(*args[1], pos); state.forceList(*args[1], pos);
if (args[0]->listSize() != args[1]->listSize()) if (args[0]->listSize() != args[1]->listSize())
@ -3808,6 +3902,7 @@ static RegisterPrimOp primop_replaceStrings({
static void prim_parseDrvName(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_parseDrvName(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "parseDrvName");
auto name = state.forceStringNoCtx(*args[0], pos); auto name = state.forceStringNoCtx(*args[0], pos);
DrvName parsed(name); DrvName parsed(name);
auto attrs = state.buildBindings(2); auto attrs = state.buildBindings(2);
@ -3832,6 +3927,7 @@ static RegisterPrimOp primop_parseDrvName({
static void prim_compareVersions(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_compareVersions(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "compareVersions");
auto version1 = state.forceStringNoCtx(*args[0], pos); auto version1 = state.forceStringNoCtx(*args[0], pos);
auto version2 = state.forceStringNoCtx(*args[1], pos); auto version2 = state.forceStringNoCtx(*args[1], pos);
v.mkInt(compareVersions(version1, version2)); v.mkInt(compareVersions(version1, version2));
@ -3852,6 +3948,7 @@ static RegisterPrimOp primop_compareVersions({
static void prim_splitVersion(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_splitVersion(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
NIX_TRACE(state, state.positions[pos], "splitVersion");
auto version = state.forceStringNoCtx(*args[0], pos); auto version = state.forceStringNoCtx(*args[0], pos);
auto iter = version.cbegin(); auto iter = version.cbegin();
Strings components; Strings components;

20
src/libexpr/tracing.cc Normal file
View File

@ -0,0 +1,20 @@
#include "tracing.hh"
static uint64_t trace_counter = 0;
namespace nix {
TraceData::TraceData(Pos p, const char* t):
// If the origin is a String don't use the location as
// otherwise we emit the entire input string as "file".
file((p.origin == foString) ? "<string>" :
((p.origin == foStdin) ? "<stdin>" : p.file)),
type(t),
line((p.origin == foString || p.origin == foStdin) ? 0 : p.line),
id(trace_counter++),
invalid(false)
{}
void TraceData::print(std::ostream & os) {
os << id << " " << (file.size() > 0 ? file : "<undefined>") << " " << (type ? type : "n/a") << " " << line << (invalid ? " invalid" : "");
}
}

129
src/libexpr/tracing.hh Normal file
View File

@ -0,0 +1,129 @@
#pragma once
#include <ctime>
#include <memory>
#include <string>
#include "nixexpr.hh"
namespace nix {
struct TraceData {
std::string file;
const char * type;
size_t line;
uint64_t id;
bool invalid;
TraceData() : file(""), type(""), line(0), id(0), invalid(true) {}
TraceData(Pos p, const char* t);
void print(std::ostream & os);
};
template<typename Data, size_t size>
struct TracingChunk {
struct Entry {
uint64_t ts_entry;
uint64_t ts_exit;
Data data;
};
struct EntryRAII {
Entry* e;
// Generate a 64bit unsigned integer reprsenting the current
// time in nanoseconds
static inline uint64_t now() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
const auto ns = uint64_t(ts.tv_nsec);
const auto s = (uint64_t(ts.tv_sec) * 1000000000);
return s + ns;
}
EntryRAII(Entry* e, Data d) : e(e) {
e->ts_entry = now();
e->data = d;
}
~EntryRAII() {
auto n = now();
e->ts_exit = n;
}
Entry* operator->() {
return e;
}
};
Entry data[size];
size_t pos;
TracingChunk(): pos(0) {}
inline bool has_capacity() const {
return pos < size - 1;
}
inline EntryRAII create(Data d) {
// assert(has_capacity()); -- we are writing C++ to go fast, who cares about correctness?!?
auto e = &data[pos++];
return EntryRAII(e, d);
}
};
template <typename Data, size_t chunk_size=4096>
struct TracingBuffer {
typedef TracingChunk<Data, chunk_size> TC;
// Linked-list of all the chunks that we know about, the last chunk in the list is the latest
std::list<TC> chunks;
TC* current_chunk; // FIXME: undefined before alloc_new_chunk
TracingBuffer() : current_chunk(NULL) {
alloc_next_chunk();
}
inline void alloc_next_chunk() {
current_chunk = &chunks.emplace_back(TC());
}
inline typename TC::EntryRAII create(Data d) {
if (!current_chunk->has_capacity()) [[unlikely]] {
alloc_next_chunk();
}
return current_chunk->create(d);
}
};
// FIXME: move this to the header file and the EvalState type so we
// don't use a global state to do tracing.
typedef TracingBuffer<TraceData> TracingBufferT;
// RAII container to ensure that exiting the call actually calls the destructor,
// actual type is std::optional<TracingChunk::EntryRAII>
#define NIX_TRACE(es, pos, type) \
std::optional<TracingBufferT::TC::EntryRAII> __traceRAII = {}; \
{ \
if ((es).tracingBuffer) [[unlikely]] { \
__traceRAII = \
std::optional((es).tracingBuffer->create(TraceData(pos, type))); \
} \
}
// create a "trace point" by passing an eval state reference and an expression
// ptr
#define NIX_TRACE_ES(es, e) NIX_TRACE((es),(es).positions[(e)->getPos()], (e)->showExprType())
// create a top-level trace-point, for usage within the EvalState class. Assumes
// `positions` is in scope.
#define NIX_TRACE_TOP(es,e) NIX_TRACE((es), (es).positions[e->getPos()], "top-level")
}

View File

@ -81,7 +81,8 @@ void processExpr(EvalState & state, const Strings & attrPaths,
if (store2) if (store2)
drvPathS = store2->addPermRoot(drvPath, rootName); drvPathS = store2->addPermRoot(drvPath, rootName);
} }
std::cout << fmt("%s%s\n", drvPathS, (outputName != "out" ? "!" + outputName : "")); // THis is polluting the nice trace we're generating, boo.
// std::cout << fmt("%s%s\n", drvPathS, (outputName != "out" ? "!" + outputName : ""));
} }
} }
} }
@ -190,6 +191,7 @@ static int main_nix_instantiate(int argc, char * * argv)
evalOnly, outputKind, xmlOutputSourceLocation, e); evalOnly, outputKind, xmlOutputSourceLocation, e);
} }
state->printTraces();
state->printStats(); state->printStats();
return 0; return 0;