Compare commits

...

6 Commits

Author SHA1 Message Date
Félix Baylac Jacqué 453285fd1b BUG: line and columns in file location are segfaulting tracy
I don't really get that one. As soon as we send those to tracy, we
segfault the profiler. I really don't get what's happening here.

We need to investigate this more to figure out what's happening.
2024-02-08 09:36:09 +01:00
Félix Baylac Jacqué a53425cac4 Tracy: autotools setup
Well, this was as painful as expected.

Introducing a proper autotool-based tracy build. I don't think this is
the right approach, but hey, it works.

Tracy is not distributing any pkg-config file, so we had to fallback
to a more "manual" method to propagate the headers path. Instead of
having a plain enable/disable flag, we send the path to the tracy
/public subfolder (~= $out in the Tracy Nix derivation).

The flake Nixpkgs is currently pointing to 23.05. The tracy derivation
was only building the visualizer, not the client library back then. We
introduce a 23.11 Nixpkgs and build its Tracy derivation with the
23.05 toolchain to go around some glibc ABI incompatibilities.

Kudos to Mic92 for the help for the flake bit.

Co-authored-by: Jörg Thalheim <joerg@thalheim.io>
2024-02-08 09:29:25 +01:00
Félix Baylac Jacqué 376d9dd520 Tracing primops
Tracing the primops with Tracy as well. Not sure it really worth the
overhead, but we still can revisit this decision later if we realize
primops are not that important perf-wise.
2024-02-08 09:27:50 +01:00
Félix Baylac Jacqué b35cc131af Tracing EvalState members
Two expressions evaluations seem to be contained in the EvalState
class. Tracing them as well through a new macro.
2024-02-08 09:26:55 +01:00
Félix Baylac Jacqué f2e832d0f8 Trace the eval.cc functions with Tracy
We're leveraging the ZoneTransientN macro to send dynamic strings
containing the expression type, file and position in that file of the
expression Nix is currently evaluating.

We had to add a new showExprType method to the Expr class to get a
const string containing the name of an expression.
2024-02-08 09:23:15 +01:00
Félix Baylac Jacqué 0905dea80f Naive build system setup
This is a first quick-and-dirty way to get the tracy integration up
and running without losing my sanity trying to bend the autotools
setup to work with it.

Introducing a tracy submodule, injecting the required headers from
here, building the TracyClient.cpp file to get the actual tracy client
library.
2024-02-08 09:21:04 +01:00
12 changed files with 224 additions and 7 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "src/libexpr/tracy"]
path = src/libexpr/tracy
url = git@github.com:wolfpld/tracy.git

View File

@ -14,6 +14,7 @@ ENABLE_FUNCTIONAL_TESTS = @ENABLE_FUNCTIONAL_TESTS@
ENABLE_INTERNAL_API_DOCS = @ENABLE_INTERNAL_API_DOCS@
ENABLE_S3 = @ENABLE_S3@
ENABLE_UNIT_TESTS = @ENABLE_UNIT_TESTS@
ENABLE_TRACY = @ENABLE_TRACY@
GTEST_LIBS = @GTEST_LIBS@
HAVE_LIBCPUID = @HAVE_LIBCPUID@
HAVE_SECCOMP = @HAVE_SECCOMP@

View File

@ -348,6 +348,15 @@ if test "$gc" = yes; then
AC_DEFINE(HAVE_BOEHMGC, 1, [Whether to use the Boehm garbage collector.])
fi
AC_ARG_ENABLE(tracy-profiler, AS_HELP_STRING([--enable-tracy-profiler],[Profile the Nix evaluation using the Tracy profiler (default no)]),
TRACY_PROFILER=$enableval, TRACY_PROFILER=no)
if test "$TRACY_PROFILER" != no; then
# We don't have any pkg-config file for tracy, we have to inject the
# headers manually…
CXXFLAGS="-I $TRACY_PROFILER/include/Tracy $CXXFLAGS"
fi
AC_SUBST(TRACY_PROFILER)
AS_IF([test "$ENABLE_UNIT_TESTS" == "yes"],[
# Look for gtest.

View File

@ -64,12 +64,29 @@
"type": "github"
}
},
"nixpkgs-tracy": {
"locked": {
"lastModified": 1707205788,
"narHash": "sha256-dFPctGYh7cNPAuJQY9i4+1/M+LdrRDpk0RRwpClEQ4w=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e311f423a6e56505afae9d4b621759826e7270c0",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.11-small",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-compat": "flake-compat",
"libgit2": "libgit2",
"nixpkgs": "nixpkgs",
"nixpkgs-regression": "nixpkgs-regression"
"nixpkgs-regression": "nixpkgs-regression",
"nixpkgs-tracy": "nixpkgs-tracy"
}
}
},

View File

@ -2,11 +2,14 @@
description = "The purely functional package manager";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05-small";
# Tracy is in a half-broken situation on 23.05. We need the changes
# introduced by https://github.com/NixOS/nixpkgs/pull/261589.
inputs.nixpkgs-tracy.url = "github:NixOS/nixpkgs/nixos-23.11-small";
inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
inputs.libgit2 = { url = "github:libgit2/libgit2"; flake = false; };
outputs = { self, nixpkgs, nixpkgs-regression, libgit2, ... }:
outputs = { self, nixpkgs, nixpkgs-regression, libgit2, nixpkgs-tracy, ... }:
let
inherit (nixpkgs) lib;
@ -186,7 +189,18 @@
stdenv
versionSuffix
;
# We have to use the nixos-23.11 tracy version. However,
# we can't link the 23.11 lib on our 23.05 nix without
# hitting some Glibc ABI incompatibilities.
#
# To go around that issue, we build the 23.11 tracy
# derivation with the 23.05 toolchain/libs.
tracy = final.callPackage (nixpkgs-tracy.legacyPackages.${final.hostPlatform.system}.path + "/pkgs/development/tools/tracy") {};
enableTracy = true;
officialRelease = false;
# The test suite fails. Unrelated to our changes.
doCheck = false;
doInstallCheck = false;
boehmgc = final.boehmgc-nix;
libgit2 = final.libgit2-nix;
busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell;
@ -223,6 +237,10 @@
self.packages.${system}.nix.override { enableGC = false; }
);
buildWithTracy = forAllSystems (system:
self.packages.${system}.nix.override { enableTracy = true; }
);
buildNoTests = forAllSystems (system:
self.packages.${system}.nix.override {
doCheck = false;
@ -394,7 +412,7 @@
stdenvs)));
devShells = let
makeShell = pkgs: stdenv: (pkgs.nix.override { inherit stdenv; forDevShell = true; }).overrideAttrs (attrs: {
makeShell = pkgs: stdenv: (pkgs.nix.override { inherit stdenv; forDevShell = true; enableTracy = true; }).overrideAttrs (attrs: {
installFlags = "sysconfdir=$(out)/etc";
shellHook = ''
PATH=$prefix/bin:$PATH

View File

@ -34,6 +34,7 @@
, rapidcheck
, sqlite
, util-linux
, tracy
, xz
, busybox-sandbox-shell ? null
@ -76,6 +77,9 @@
# only leaked *within* the process.)
, enableGC ? true
# TODO
, enableTracy ? false
# Whether to enable Markdown rendering in the Nix binary.
, enableMarkdown ? !stdenv.hostPlatform.isWindows
@ -234,6 +238,7 @@ in {
rapidcheck
] ++ lib.optional stdenv.isLinux libseccomp
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid
++ lib.optional enableTracy tracy
# There have been issues building these dependencies
++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin))
(aws-sdk-cpp.override {
@ -287,6 +292,8 @@ in {
(lib.enableFeature enableMarkdown "markdown")
(lib.enableFeature installUnitTests "install-unit-tests")
(lib.withFeatureAs true "readline-flavor" readlineFlavor)
] ++ lib.optionals (enableTracy) [
"--enable-tracy-profiler=${tracy}"
] ++ lib.optionals (!forDevShell) [
"--sysconfdir=/etc"
] ++ lib.optionals installUnitTests [

View File

@ -25,6 +25,7 @@
#include "flake/flakeref.hh"
#include "parser-tab.hh"
#include <tracy/Tracy.hpp>
#include <algorithm>
#include <chrono>
#include <iostream>
@ -54,6 +55,16 @@
#endif
#define TRACY_TRACE(evalstate, expr) \
std::ostringstream tracyss; \
tracyss << (evalstate).positions[(expr)->getPos()] << " " << (expr)->showExprType(); \
ZoneTransientN(nix, tracyss.str().c_str(), true);
#define TRACY_TRACE_TYPE_STR(evalstate, expr, typestr) \
std::ostringstream tracyss; \
tracyss << (evalstate).positions[(expr)->getPos()] << " " << typestr; \
ZoneTransientN(nix, tracyss.str().c_str(), true);
using json = nlohmann::json;
namespace nix {
@ -70,7 +81,6 @@ static char * allocString(size_t size)
return t;
}
static char * dupString(const char * s)
{
char * t;
@ -1163,6 +1173,7 @@ void EvalState::eval(Expr * e, Value & v)
inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::string_view errorCtx)
{
TRACY_TRACE_TYPE_STR(*this, e, "bool")
try {
Value v;
e->eval(*this, env, v);
@ -1181,6 +1192,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::stri
inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const PosIdx pos, std::string_view errorCtx)
{
TRACY_TRACE_TYPE_STR(*this, e, "attrs")
try {
e->eval(*this, env, v);
if (v.type() != nAttrs)
@ -1203,29 +1215,34 @@ void Expr::eval(EvalState & state, Env & env, Value & v)
void ExprInt::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
v = this->v;
}
void ExprFloat::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
v = this->v;
}
void ExprString::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
v = this->v;
}
void ExprPath::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
v = this->v;
}
void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish());
auto dynamicEnv = &env;
@ -1310,6 +1327,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
void ExprLet::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
/* Create a new environment that contains the attributes in this
`let'. */
Env & env2(state.allocEnv(attrs->attrs.size()));
@ -1328,6 +1346,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
void ExprList::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
state.mkList(v, elems.size());
for (auto [n, v2] : enumerate(v.listItems()))
const_cast<Value * &>(v2) = elems[n]->maybeThunk(state, env);
@ -1336,6 +1355,7 @@ void ExprList::eval(EvalState & state, Env & env, Value & v)
Value * ExprList::maybeThunk(EvalState & state, Env & env)
{
TRACY_TRACE(state, this);
if (elems.empty()) {
return &state.vEmptyList;
}
@ -1345,6 +1365,7 @@ Value * ExprList::maybeThunk(EvalState & state, Env & env)
void ExprVar::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
Value * v2 = state.lookupVar(&env, *this, false);
state.forceValue(*v2, pos);
v = *v2;
@ -1372,6 +1393,7 @@ static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & a
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this)
Value vTmp;
PosIdx pos2;
Value * vAttrs = &vTmp;
@ -1436,6 +1458,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this)
Value vTmp;
Value * vAttrs = &vTmp;
@ -1461,6 +1484,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
void ExprLambda::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this)
v.mkLambda(&env, this);
}
@ -1718,6 +1742,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
void ExprCall::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
Value vFun;
fun->eval(state, env, vFun);
@ -1795,6 +1820,7 @@ https://nixos.org/manual/nix/stable/language/constructs.html#functions.)", symbo
void ExprWith::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
Env & env2(state.allocEnv(1));
env2.up = &env;
env2.values[0] = attrs->maybeThunk(state, env);
@ -1805,6 +1831,7 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v)
void ExprIf::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
// We cheat in the parser, and pass the position of the condition as the position of the if itself.
(state.evalBool(env, cond, pos, "while evaluating a branch condition") ? then : else_)->eval(state, env, v);
}
@ -1812,6 +1839,7 @@ void ExprIf::eval(EvalState & state, Env & env, Value & v)
void ExprAssert::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
if (!state.evalBool(env, cond, pos, "in the condition of the assert statement")) {
std::ostringstream out;
cond->show(state.symbols, out);
@ -1823,12 +1851,14 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
v.mkBool(!state.evalBool(env, e, getPos(), "in the argument of the not operator")); // XXX: FIXME: !
}
void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
Value v1; e1->eval(state, env, v1);
Value v2; e2->eval(state, env, v2);
v.mkBool(state.eqValues(v1, v2, pos, "while testing two values for equality"));
@ -1837,6 +1867,7 @@ void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
void ExprOpNEq::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
Value v1; e1->eval(state, env, v1);
Value v2; e2->eval(state, env, v2);
v.mkBool(!state.eqValues(v1, v2, pos, "while testing two values for inequality"));
@ -1845,6 +1876,7 @@ void ExprOpNEq::eval(EvalState & state, Env & env, Value & v)
void ExprOpAnd::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
v.mkBool(state.evalBool(env, e1, pos, "in the left operand of the AND (&&) operator") && state.evalBool(env, e2, pos, "in the right operand of the AND (&&) operator"));
}
@ -1857,12 +1889,14 @@ void ExprOpOr::eval(EvalState & state, Env & env, Value & v)
void ExprOpImpl::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
v.mkBool(!state.evalBool(env, e1, pos, "in the left operand of the IMPL (->) operator") || state.evalBool(env, e2, pos, "in the right operand of the IMPL (->) operator"));
}
void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
Value v1, v2;
state.evalAttrs(env, e1, v1, pos, "in the left operand of the update (//) operator");
state.evalAttrs(env, e2, v2, pos, "in the right operand of the update (//) operator");
@ -1901,6 +1935,7 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
Value v1; e1->eval(state, env, v1);
Value v2; e2->eval(state, env, v2);
Value * lists[2] = { &v1, &v2 };
@ -1939,6 +1974,7 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
NixStringContext context;
std::vector<BackedStringView> s;
size_t sSize = 0;
@ -2031,12 +2067,14 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
void ExprPos::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
state.mkPos(v, pos);
}
void ExprBlackHole::eval(EvalState & state, Env & env, Value & v)
{
TRACY_TRACE(state, this);
state.error("infinite recursion encountered")
.debugThrow<InfiniteRecursionError>();
}

View File

@ -20,6 +20,12 @@ libexpr_LDFLAGS += -lboost_context $(THREAD_LDFLAGS)
ifdef HOST_LINUX
libexpr_LDFLAGS += -ldl
endif
ifneq ($(TRACY_PROFILER), no)
libexpr_LDFLAGS += -ltracy
# We have to set TRACY_ENABLE to have tracy actually send the trace
# events, it's no-op without them.
libexpr_CXXFLAGS += -DTRACY_ENABLE=1
endif
# The dependency on libgc must be propagated (i.e. meaning that
# programs/libraries that use libexpr must explicitly pass -lgc),
@ -48,3 +54,4 @@ $(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh
$(d)/eval.cc: $(d)/primops/derivation.nix.gen.hh $(d)/fetchurl.nix.gen.hh $(d)/flake/call-flake.nix.gen.hh
$(buildprefix)src/libexpr/primops/fromTOML.o: ERROR_SWITCH_ENUM =
$(buildprefix)src/libexpr/tracy/public/TracyClient.o: ERROR_SWITCH_ENUM =

View File

@ -156,6 +156,7 @@ struct Expr
virtual Value * maybeThunk(EvalState & state, Env & env);
virtual void setName(Symbol name);
virtual PosIdx getPos() const { return noPos; }
virtual const char* showExprType() const { return "undefined"; }
};
#define COMMON_METHODS \
@ -168,6 +169,7 @@ struct ExprInt : Expr
Value v;
ExprInt(NixInt n) { v.mkInt(n); };
Value * maybeThunk(EvalState & state, Env & env) override;
const char* showExprType() const { return "int"; }
COMMON_METHODS
};
@ -176,6 +178,7 @@ struct ExprFloat : Expr
Value v;
ExprFloat(NixFloat nf) { v.mkFloat(nf); };
Value * maybeThunk(EvalState & state, Env & env) override;
const char* showExprType() const { return "float"; }
COMMON_METHODS
};
@ -185,6 +188,7 @@ struct ExprString : Expr
Value v;
ExprString(std::string &&s) : s(std::move(s)) { v.mkString(this->s.data()); };
Value * maybeThunk(EvalState & state, Env & env) override;
const char* showExprType() const { return "string"; }
COMMON_METHODS
};
@ -198,6 +202,7 @@ struct ExprPath : Expr
v.mkPath(&*accessor, this->s.c_str());
}
Value * maybeThunk(EvalState & state, Env & env) override;
const char* showExprType() const { return "path"; }
COMMON_METHODS
};
@ -229,6 +234,7 @@ struct ExprVar : Expr
ExprVar(const PosIdx & pos, Symbol name) : pos(pos), name(name) { };
Value * maybeThunk(EvalState & state, Env & env) override;
PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "var"; }
COMMON_METHODS
};
@ -240,6 +246,7 @@ struct ExprSelect : Expr
ExprSelect(const PosIdx & pos, Expr * e, AttrPath attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(std::move(attrPath)) { };
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; }
const char* showExprType() const { return "select"; }
COMMON_METHODS
};
@ -249,6 +256,7 @@ struct ExprOpHasAttr : Expr
AttrPath attrPath;
ExprOpHasAttr(Expr * e, AttrPath attrPath) : e(e), attrPath(std::move(attrPath)) { };
PosIdx getPos() const override { return e->getPos(); }
const char* showExprType() const { return "op_has_attr"; }
COMMON_METHODS
};
@ -278,6 +286,7 @@ struct ExprAttrs : Expr
ExprAttrs(const PosIdx &pos) : recursive(false), pos(pos) { };
ExprAttrs() : recursive(false) { };
PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "attrs"; }
COMMON_METHODS
};
@ -285,6 +294,7 @@ struct ExprList : Expr
{
std::vector<Expr *> elems;
ExprList() { };
const char* showExprType() const { return "list"; }
COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env) override;
@ -345,6 +355,7 @@ struct ExprLambda : Expr
std::string showNamePos(const EvalState & state) const;
inline bool hasFormals() const { return formals != nullptr; }
PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "lambda"; }
COMMON_METHODS
};
@ -357,6 +368,7 @@ struct ExprCall : Expr
: fun(fun), args(args), pos(pos)
{ }
PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "call"; }
COMMON_METHODS
};
@ -365,6 +377,7 @@ struct ExprLet : Expr
ExprAttrs * attrs;
Expr * body;
ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { };
const char* showExprType() const { return "let"; }
COMMON_METHODS
};
@ -376,6 +389,7 @@ struct ExprWith : Expr
ExprWith * parentWith;
ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "with"; }
COMMON_METHODS
};
@ -385,6 +399,7 @@ struct ExprIf : Expr
Expr * cond, * then, * 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; }
const char* showExprType() const { return "if"; }
COMMON_METHODS
};
@ -394,6 +409,7 @@ struct ExprAssert : Expr
Expr * cond, * body;
ExprAssert(const PosIdx & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "assert"; }
COMMON_METHODS
};
@ -422,6 +438,7 @@ struct ExprOpNot : Expr
} \
void eval(EvalState & state, Env & env, Value & v) override; \
PosIdx getPos() const override { return pos; } \
const char* showExprType() const { return #name; } \
};
MakeBinOp(ExprOpEq, "==")
@ -440,6 +457,7 @@ struct ExprConcatStrings : Expr
ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector<std::pair<PosIdx, Expr *>> * es)
: pos(pos), forceString(forceString), es(es) { };
PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "concat_strings"; }
COMMON_METHODS
};
@ -448,6 +466,7 @@ struct ExprPos : Expr
PosIdx pos;
ExprPos(const PosIdx & pos) : pos(pos) { };
PosIdx getPos() const override { return pos; }
const char* showExprType() const { return "pos"; }
COMMON_METHODS
};
@ -457,6 +476,7 @@ struct ExprBlackHole : Expr
void show(const SymbolTable & symbols, std::ostream & str) const override {}
void eval(EvalState & state, Env & env, Value & v) override;
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override {}
const char* showExprType() const { return "blackhole"; }
};
extern ExprBlackHole eBlackHole;

View File

@ -32,6 +32,13 @@
#include <cmath>
#include <tracy/Tracy.hpp>
#define TRACY_TRACE_PRIMOP(es, posidx, typestr) \
std::ostringstream tracyss; \
tracyss << es.positions[posidx] << " " << typestr; \
ZoneTransientN(nix, tracyss.str().c_str(), true);
namespace nix {
@ -120,6 +127,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context)
static SourcePath realisePath(EvalState & state, const PosIdx pos, Value & v, bool resolveSymlinks = true)
{
TRACY_TRACE_PRIMOP(state, pos, "realizePath");
NixStringContext context;
auto path = state.coerceToPath(noPos, v, context, "while realising the context of a path");
@ -170,6 +178,7 @@ static void mkOutputString(
argument. */
static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * vScope, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.import");
auto path = realisePath(state, pos, vPath, false);
auto path2 = path.path.abs();
@ -334,6 +343,7 @@ extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v);
/* Load a ValueInitializer from a DSO and return whatever it initializes */
void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.importNative");
auto path = realisePath(state, pos, *args[0]);
std::string sym(state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument passed to builtins.importNative"));
@ -361,6 +371,7 @@ void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Valu
/* Execute a program and parse its output */
void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.exec");
state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.exec");
auto elems = args[0]->listElems();
auto count = args[0]->listSize();
@ -402,6 +413,7 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v)
/* Return a string representing the type of the expression. */
static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.typeOf");
state.forceValue(*args[0], pos);
std::string t;
switch (args[0]->type()) {
@ -436,6 +448,7 @@ static RegisterPrimOp primop_typeOf({
/* Determine whether the argument is the null value. */
static void prim_isNull(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.isNull");
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nNull);
}
@ -454,6 +467,7 @@ static RegisterPrimOp primop_isNull({
/* Determine whether the argument is a function. */
static void prim_isFunction(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.isFunction");
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nFunction);
}
@ -470,6 +484,7 @@ static RegisterPrimOp primop_isFunction({
/* Determine whether the argument is an integer. */
static void prim_isInt(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.isInt");
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nInt);
}
@ -486,6 +501,7 @@ static RegisterPrimOp primop_isInt({
/* Determine whether the argument is a float. */
static void prim_isFloat(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.isFloat");
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nFloat);
}
@ -502,6 +518,7 @@ static RegisterPrimOp primop_isFloat({
/* Determine whether the argument is a string. */
static void prim_isString(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.isString");
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nString);
}
@ -518,6 +535,7 @@ static RegisterPrimOp primop_isString({
/* Determine whether the argument is a Boolean. */
static void prim_isBool(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.isBool");
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nBool);
}
@ -534,6 +552,7 @@ static RegisterPrimOp primop_isBool({
/* Determine whether the argument is a path. */
static void prim_isPath(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.isPath");
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nPath);
}
@ -644,6 +663,7 @@ static Bindings::iterator getAttr(
static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.genericClosure");
state.forceAttrs(*args[0], noPos, "while evaluating the first argument passed to builtins.genericClosure");
/* Get the start set. */
@ -754,6 +774,7 @@ static RegisterPrimOp primop_break({
)",
.fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.break");
if (state.debugRepl && !state.debugTraces.empty()) {
auto error = Error(ErrorInfo {
.level = lvlInfo,
@ -787,6 +808,7 @@ static RegisterPrimOp primop_abort({
)",
.fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.abort");
NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context,
"while evaluating the error message passed to builtins.abort").toOwned();
@ -806,6 +828,7 @@ static RegisterPrimOp primop_throw({
)",
.fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.throw");
NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context,
"while evaluating the error message passed to builtin.throw").toOwned();
@ -815,6 +838,7 @@ static RegisterPrimOp primop_throw({
static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.addErrorContext");
try {
state.forceValue(*args[1], pos);
v = *args[1];
@ -836,6 +860,7 @@ static RegisterPrimOp primop_addErrorContext(PrimOp {
static void prim_ceil(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.ceil");
auto value = state.forceFloat(*args[0], args[0]->determinePos(pos),
"while evaluating the first argument passed to builtins.ceil");
v.mkInt(ceil(value));
@ -856,6 +881,7 @@ static RegisterPrimOp primop_ceil({
static void prim_floor(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.floor");
auto value = state.forceFloat(*args[0], args[0]->determinePos(pos), "while evaluating the first argument passed to builtins.floor");
v.mkInt(floor(value));
}
@ -877,6 +903,7 @@ static RegisterPrimOp primop_floor({
* else => {success=false; value=false;} */
static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.tryEval");
auto attrs = state.buildBindings(2);
/* increment state.trylevel, and decrement it when this function returns. */
@ -930,6 +957,7 @@ static RegisterPrimOp primop_tryEval({
/* Return an environment variable. Use with care. */
static void prim_getEnv(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.getEnv");
std::string name(state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.getEnv"));
v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or(""));
}
@ -954,6 +982,7 @@ static RegisterPrimOp primop_getEnv({
/* Evaluate the first argument, then return the second argument. */
static void prim_seq(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.seq");
state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos);
v = *args[1];
@ -973,6 +1002,7 @@ static RegisterPrimOp primop_seq({
attrsets), then return the second argument. */
static void prim_deepSeq(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.deepSeq");
state.forceValueDeep(*args[0]);
state.forceValue(*args[1], pos);
v = *args[1];
@ -993,6 +1023,7 @@ static RegisterPrimOp primop_deepSeq({
return the second expression. Useful for debugging. */
static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.trace");
state.forceValue(*args[0], pos);
if (args[0]->type() == nString)
printError("trace: %1%", args[0]->string_view());
@ -1019,6 +1050,7 @@ static RegisterPrimOp primop_trace({
*/
static void prim_second(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.second");
state.forceValue(*args[1], pos);
v = *args[1];
}
@ -1038,6 +1070,7 @@ static void derivationStrictInternal(EvalState & state, const std::string & name
derivation. */
static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.derivationStrict");
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.derivationStrict");
Bindings * attrs = args[0]->attrs;
@ -1436,6 +1469,7 @@ static RegisterPrimOp primop_derivationStrict(PrimOp {
out. */
static void prim_placeholder(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.placeholder");
v.mkString(hashPlaceholder(state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.placeholder")));
}
@ -1459,6 +1493,7 @@ static RegisterPrimOp primop_placeholder({
/* Convert the argument to a path. !!! obsolete? */
static void prim_toPath(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.toPath");
NixStringContext context;
auto path = state.coerceToPath(pos, *args[0], context, "while evaluating the first argument passed to builtins.toPath");
v.mkString(path.path.abs(), context);
@ -1484,6 +1519,7 @@ static RegisterPrimOp primop_toPath({
corner cases. */
static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.storePath");
if (evalSettings.pureEval)
state.debugThrowLastTrace(EvalError({
.msg = hintfmt("'%s' is not allowed in pure evaluation mode", "builtins.storePath"),
@ -1531,6 +1567,7 @@ static RegisterPrimOp primop_storePath({
static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.pathExists");
try {
auto & arg = *args[0];
@ -1563,6 +1600,7 @@ static RegisterPrimOp primop_pathExists({
following the last slash. */
static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.baseNameOf");
NixStringContext context;
v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context,
"while evaluating the first argument passed to builtins.baseNameOf",
@ -1585,6 +1623,7 @@ static RegisterPrimOp primop_baseNameOf({
of the argument. */
static void prim_dirOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.dirOf");
state.forceValue(*args[0], pos);
if (args[0]->type() == nPath) {
auto path = args[0]->path();
@ -1613,6 +1652,7 @@ static RegisterPrimOp primop_dirOf({
/* Return the contents of a file as a string. */
static void prim_readFile(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.readFile");
auto path = realisePath(state, pos, *args[0]);
auto s = path.readFile();
if (s.find((char) 0) != std::string::npos)
@ -1650,6 +1690,7 @@ static RegisterPrimOp primop_readFile({
which are desugared to 'findFile __nixPath "x"'. */
static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.findFile");
state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.findFile");
SearchPath searchPath;
@ -1742,6 +1783,7 @@ static RegisterPrimOp primop_findFile(PrimOp {
/* Return the cryptographic hash of a file in base-16. */
static void prim_hashFile(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.hashFile");
auto algo = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.hashFile");
std::optional<HashAlgorithm> ha = parseHashAlgo(algo);
if (!ha)
@ -1777,6 +1819,7 @@ static std::string_view fileTypeToString(InputAccessor::Type type)
static void prim_readFileType(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.readFileType");
auto path = realisePath(state, pos, *args[0], false);
/* Retrieve the directory entry type and stringize it. */
v.mkString(fileTypeToString(path.lstat().type));
@ -1795,6 +1838,7 @@ static RegisterPrimOp primop_readFileType({
/* Read a directory (without . or ..) */
static void prim_readDir(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.readDir");
auto path = realisePath(state, pos, *args[0]);
// Retrieve directory entries for all nodes in a directory.
@ -1852,6 +1896,7 @@ static RegisterPrimOp primop_readDir({
/* Extend single element string context with another output. */
static void prim_outputOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.outputOf");
SingleDerivedPath drvPath = state.coerceToSingleDerivedPath(pos, *args[0], "while evaluating the first argument to builtins.outputOf");
OutputNameView outputName = state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument to builtins.outputOf");
@ -1899,6 +1944,7 @@ static RegisterPrimOp primop_outputOf({
be sensibly or completely represented (e.g., functions). */
static void prim_toXML(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.toXML");
std::ostringstream out;
NixStringContext context;
printValueAsXML(state, true, false, *args[0], out, context, pos);
@ -2007,6 +2053,7 @@ static RegisterPrimOp primop_toXML({
represented (e.g., functions). */
static void prim_toJSON(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.toJSON");
std::ostringstream out;
NixStringContext context;
printValueAsJSON(state, true, *args[0], pos, out, context);
@ -2030,6 +2077,7 @@ static RegisterPrimOp primop_toJSON({
/* Parse a JSON string to a value. */
static void prim_fromJSON(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.fromJSON");
auto s = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.fromJSON");
try {
parseJSON(state, s, v);
@ -2058,6 +2106,7 @@ static RegisterPrimOp primop_fromJSON({
as an input by derivations. */
static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.toFile");
NixStringContext context;
std::string name(state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.toFile"));
std::string contents(state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.toFile"));
@ -2207,6 +2256,7 @@ static void addPath(
Value & v,
const NixStringContext & context)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.addPath");
try {
StorePathSet refs;
@ -2256,6 +2306,7 @@ static void addPath(
static void prim_filterSource(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.filterSource");
NixStringContext context;
auto path = state.coerceToPath(pos, *args[1], context,
"while evaluating the second argument (the path to filter) passed to 'builtins.filterSource'");
@ -2321,6 +2372,7 @@ static RegisterPrimOp primop_filterSource({
static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.path");
std::optional<SourcePath> path;
std::string name;
Value * filterFun = nullptr;
@ -2404,6 +2456,7 @@ static RegisterPrimOp primop_path({
strings. */
static void prim_attrNames(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.attrNames");
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrNames");
state.mkList(v, args[0]->attrs->size());
@ -2431,6 +2484,7 @@ static RegisterPrimOp primop_attrNames({
order as attrNames. */
static void prim_attrValues(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.attrValues");
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrValues");
state.mkList(v, args[0]->attrs->size());
@ -2463,6 +2517,7 @@ static RegisterPrimOp primop_attrValues({
/* Dynamic version of the `.' operator. */
void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.getAttr");
auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.getAttr");
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.getAttr");
Bindings::iterator i = getAttr(
@ -2492,6 +2547,7 @@ static RegisterPrimOp primop_getAttr({
/* Return position information of the specified attribute. */
static void prim_unsafeGetAttrPos(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.unsafeGetAttrPos");
auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.unsafeGetAttrPos");
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.unsafeGetAttrPos");
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
@ -2510,6 +2566,7 @@ static RegisterPrimOp primop_unsafeGetAttrPos(PrimOp {
/* Dynamic version of the `?' operator. */
static void prim_hasAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.hasAttr");
auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.hasAttr");
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.hasAttr");
v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end());
@ -2544,6 +2601,7 @@ static RegisterPrimOp primop_isAttrs({
static void prim_removeAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.removeAttrs");
state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.removeAttrs");
state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.removeAttrs");
@ -2593,6 +2651,7 @@ static RegisterPrimOp primop_removeAttrs({
name, the first takes precedence. */
static void prim_listToAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.listToAttrs");
state.forceList(*args[0], pos, "while evaluating the argument passed to builtins.listToAttrs");
auto attrs = state.buildBindings(args[0]->listSize());
@ -2649,6 +2708,7 @@ static RegisterPrimOp primop_listToAttrs({
static void prim_intersectAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.intersectAttrs");
state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.intersectAttrs");
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.intersectAttrs");
@ -2727,6 +2787,7 @@ static RegisterPrimOp primop_intersectAttrs({
static void prim_catAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.catAttrs");
auto attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.catAttrs"));
state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.catAttrs");
@ -2764,6 +2825,7 @@ static RegisterPrimOp primop_catAttrs({
static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.functionArgs");
state.forceValue(*args[0], pos);
if (args[0]->isPrimOpApp() || args[0]->isPrimOp()) {
v.mkAttrs(&state.emptyBindings);
@ -2807,6 +2869,7 @@ static RegisterPrimOp primop_functionArgs({
/* */
static void prim_mapAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.mapAttrs");
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.mapAttrs");
auto attrs = state.buildBindings(args[1]->attrs->size());
@ -2839,6 +2902,7 @@ static RegisterPrimOp primop_mapAttrs({
static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.zipAttrsWith");
// 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
// appropriate sizes, stash the pointers to the list elements of each,
@ -2926,6 +2990,7 @@ static RegisterPrimOp primop_zipAttrsWith({
/* Determine whether the argument is a list. */
static void prim_isList(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.isList");
state.forceValue(*args[0], pos);
v.mkBool(args[0]->type() == nList);
}
@ -2954,6 +3019,7 @@ static void elemAt(EvalState & state, const PosIdx pos, Value & list, int n, Val
/* Return the n-1'th element of a list. */
static void prim_elemAt(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.elemAt");
elemAt(state, pos, *args[0], state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.elemAt"), v);
}
@ -2970,6 +3036,7 @@ static RegisterPrimOp primop_elemAt({
/* Return the first element of a list. */
static void prim_head(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.head");
elemAt(state, pos, *args[0], 0, v);
}
@ -2989,6 +3056,7 @@ static RegisterPrimOp primop_head({
don't want to use it! */
static void prim_tail(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.tail");
state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.tail");
if (args[0]->listSize() == 0)
state.debugThrowLastTrace(Error({
@ -3020,6 +3088,7 @@ static RegisterPrimOp primop_tail({
/* Apply a function to every element of a list. */
static void prim_map(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.map");
state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.map");
if (args[1]->listSize() == 0) {
@ -3056,6 +3125,7 @@ static RegisterPrimOp primop_map({
returns true. */
static void prim_filter(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.filter");
state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.filter");
if (args[1]->listSize() == 0) {
@ -3099,6 +3169,7 @@ static RegisterPrimOp primop_filter({
/* Return true if a list contains a given element. */
static void prim_elem(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.elem");
bool res = false;
state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.elem");
for (auto elem : args[1]->listItems())
@ -3122,6 +3193,7 @@ static RegisterPrimOp primop_elem({
/* Concatenate a list of lists. */
static void prim_concatLists(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.concatLists");
state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.concatLists");
state.concatLists(v, args[0]->listSize(), args[0]->listElems(), pos, "while evaluating a value of the list passed to builtins.concatLists");
}
@ -3138,6 +3210,7 @@ static RegisterPrimOp primop_concatLists({
/* 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)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.length");
state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.length");
v.mkInt(args[0]->listSize());
}
@ -3155,6 +3228,7 @@ static RegisterPrimOp primop_length({
right. The operator is applied strictly. */
static void prim_foldlStrict(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.foldStrict");
state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.foldlStrict");
state.forceList(*args[2], pos, "while evaluating the third argument passed to builtins.foldlStrict");
@ -3218,6 +3292,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)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.any");
anyOrAll(true, state, pos, args, v);
}
@ -3233,6 +3308,7 @@ static RegisterPrimOp primop_any({
static void prim_all(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.all");
anyOrAll(false, state, pos, args, v);
}
@ -3248,6 +3324,7 @@ static RegisterPrimOp primop_all({
static void prim_genList(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.genList");
auto len = state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.genList");
if (len < 0)
@ -3286,6 +3363,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)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.sort");
state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.sort");
auto len = args[1]->listSize();
@ -3345,6 +3423,7 @@ static RegisterPrimOp primop_sort({
static void prim_partition(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.partition");
state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.partition");
state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.partition");
@ -3405,6 +3484,7 @@ static RegisterPrimOp primop_partition({
static void prim_groupBy(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.groupBy");
state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.groupBy");
state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.groupBy");
@ -3457,6 +3537,7 @@ static RegisterPrimOp primop_groupBy({
static void prim_concatMap(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.concatMap");
state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.concatMap");
state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.concatMap");
auto nrLists = args[1]->listSize();
@ -3500,6 +3581,7 @@ static RegisterPrimOp primop_concatMap({
static void prim_add(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.add");
state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos);
if (args[0]->type() == nFloat || args[1]->type() == nFloat)
@ -3521,6 +3603,7 @@ static RegisterPrimOp primop_add({
static void prim_sub(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.sub");
state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos);
if (args[0]->type() == nFloat || args[1]->type() == nFloat)
@ -3542,6 +3625,7 @@ static RegisterPrimOp primop_sub({
static void prim_mul(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.mul");
state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos);
if (args[0]->type() == nFloat || args[1]->type() == nFloat)
@ -3563,6 +3647,7 @@ static RegisterPrimOp primop_mul({
static void prim_div(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.div");
state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos);
@ -3600,6 +3685,7 @@ static RegisterPrimOp primop_div({
static void prim_bitAnd(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.bitAnd");
v.mkInt(state.forceInt(*args[0], pos, "while evaluating the first argument passed to builtins.bitAnd")
& state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.bitAnd"));
}
@ -3645,6 +3731,7 @@ static RegisterPrimOp primop_bitXor({
static void prim_lessThan(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.lessThan");
state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos);
// pos is exact here, no need for a message.
@ -3674,6 +3761,7 @@ static RegisterPrimOp primop_lessThan({
`"/nix/store/whatever..."'. */
static void prim_toString(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.toString");
NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context,
"while evaluating the first argument passed to builtins.toString",
@ -3711,6 +3799,7 @@ static RegisterPrimOp primop_toString({
non-negative. */
static void prim_substring(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.substring");
int start = state.forceInt(*args[0], pos, "while evaluating the first argument (the start offset) passed to builtins.substring");
if (start < 0)
@ -3761,6 +3850,7 @@ static RegisterPrimOp primop_substring({
static void prim_stringLength(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.stringLength");
NixStringContext context;
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.stringLength");
v.mkInt(s->size());
@ -3779,6 +3869,7 @@ static RegisterPrimOp primop_stringLength({
/* Return the cryptographic hash of a string in base-16. */
static void prim_hashString(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.hashString");
auto algo = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.hashString");
std::optional<HashAlgorithm> ha = parseHashAlgo(algo);
if (!ha)
@ -3806,6 +3897,7 @@ static RegisterPrimOp primop_hashString({
static void prim_convertHash(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.convertHash");
state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.convertHash");
auto &inputAttrs = args[0]->attrs;
@ -3923,6 +4015,7 @@ std::shared_ptr<RegexCache> makeRegexCache()
void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.match");
auto re = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.match");
try {
@ -4003,6 +4096,7 @@ static RegisterPrimOp primop_match({
non-matching parts interleaved by the lists of the matching groups. */
void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.split");
auto re = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.split");
try {
@ -4106,6 +4200,7 @@ static RegisterPrimOp primop_split({
static void prim_concatStringsSep(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.concatStringsSep");
NixStringContext context;
auto sep = state.forceString(*args[0], context, pos, "while evaluating the first argument (the separator string) passed to builtins.concatStringsSep");
@ -4136,6 +4231,7 @@ static RegisterPrimOp primop_concatStringsSep({
static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.replaceStrings");
state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.replaceStrings");
state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.replaceStrings");
if (args[0]->listSize() != args[1]->listSize())
@ -4218,6 +4314,7 @@ static RegisterPrimOp primop_replaceStrings({
static void prim_parseDrvName(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.parseDrvName");
auto name = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.parseDrvName");
DrvName parsed(name);
auto attrs = state.buildBindings(2);
@ -4242,6 +4339,7 @@ static RegisterPrimOp primop_parseDrvName({
static void prim_compareVersions(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.compareVersions");
auto version1 = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.compareVersions");
auto version2 = state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument passed to builtins.compareVersions");
v.mkInt(compareVersions(version1, version2));
@ -4262,6 +4360,7 @@ static RegisterPrimOp primop_compareVersions({
static void prim_splitVersion(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
TRACY_TRACE_PRIMOP(state, pos, "primop.splitVersion");
auto version = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.splitVersion");
auto iter = version.cbegin();
Strings components;

1
src/libexpr/tracy Submodule

@ -0,0 +1 @@
Subproject commit 37aff70dfa50cf6307b3fee6074d627dc2929143

View File

@ -98,9 +98,6 @@ void Pos::print(std::ostream & out, bool showOrigin) const
}, origin);
out << ":";
}
out << line;
if (column > 0)
out << ":" << column;
}
std::ostream & operator<<(std::ostream & str, const Pos & pos)