Skip to content

Commit 13c0683

Browse files
committed
Enable ethdebug debug info and output selection.
1 parent 4ab3e36 commit 13c0683

27 files changed

+1159
-21
lines changed

libevmasm/AbstractAssemblyStack.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ class AbstractAssemblyStack
4040
virtual std::string const* sourceMapping(std::string const& _contractName) const = 0;
4141
virtual std::string const* runtimeSourceMapping(std::string const& _contractName) const = 0;
4242

43+
virtual Json ethdebug(std::string const& _contractName, bool _runtime) const = 0;
44+
45+
virtual Json ethdebug() const = 0;
46+
4347
virtual Json assemblyJSON(std::string const& _contractName) const = 0;
4448
virtual std::string assemblyString(std::string const& _contractName, StringMap const& _sourceCodes) const = 0;
4549

libevmasm/Assembly.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ std::string Assembly::assemblyString(
437437
{
438438
std::ostringstream tmp;
439439
assemblyStream(tmp, _debugInfoSelection, "", _sourceCodes);
440-
return tmp.str();
440+
return (_debugInfoSelection.ethdebug ? "/// ethdebug: enabled\n" : "") + tmp.str();
441441
}
442442

443443
Json Assembly::assemblyJSON(std::map<std::string, unsigned> const& _sourceIndices, bool _includeSourceList) const

libevmasm/EVMAssemblyStack.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ std::string const* EVMAssemblyStack::runtimeSourceMapping(std::string const& _co
103103
return &m_runtimeSourceMapping;
104104
}
105105

106+
Json EVMAssemblyStack::ethdebug(std::string const& _contractName, bool _runtime) const
107+
{
108+
solAssert(_contractName == m_name);
109+
return _runtime ? m_runtimeEthdebug : m_ethdebug;
110+
}
111+
112+
Json EVMAssemblyStack::ethdebug() const
113+
{
114+
return {};
115+
}
116+
106117
Json EVMAssemblyStack::assemblyJSON(std::string const& _contractName) const
107118
{
108119
solAssert(_contractName == m_name);

libevmasm/EVMAssemblyStack.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ class EVMAssemblyStack: public AbstractAssemblyStack
5959
virtual std::string const* sourceMapping(std::string const& _contractName) const override;
6060
virtual std::string const* runtimeSourceMapping(std::string const& _contractName) const override;
6161

62+
virtual Json ethdebug(std::string const& _contractName, bool _runtime) const override;
63+
virtual Json ethdebug() const override;
64+
6265
virtual Json assemblyJSON(std::string const& _contractName) const override;
6366
virtual std::string assemblyString(std::string const& _contractName, StringMap const& _sourceCodes) const override;
6467

@@ -87,6 +90,8 @@ class EVMAssemblyStack: public AbstractAssemblyStack
8790
langutil::DebugInfoSelection m_debugInfoSelection = langutil::DebugInfoSelection::Default();
8891
std::string m_sourceMapping;
8992
std::string m_runtimeSourceMapping;
93+
Json m_ethdebug;
94+
Json m_runtimeEthdebug;
9095
};
9196

9297
} // namespace solidity::evmasm

liblangutil/DebugInfoSelection.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,22 @@ DebugInfoSelection const DebugInfoSelection::Only(bool DebugInfoSelection::* _me
4949
return result;
5050
}
5151

52+
DebugInfoSelection const DebugInfoSelection::Except(std::vector<bool DebugInfoSelection::*> const& _members) noexcept
53+
{
54+
DebugInfoSelection result = All();
55+
for (bool DebugInfoSelection::* member: _members)
56+
result.*member = false;
57+
return result;
58+
}
59+
5260
std::optional<DebugInfoSelection> DebugInfoSelection::fromString(std::string_view _input)
5361
{
5462
// TODO: Make more stuff constexpr and make it a static_assert().
5563
solAssert(componentMap().count("all") == 0, "");
5664
solAssert(componentMap().count("none") == 0, "");
5765

5866
if (_input == "all")
59-
return All();
67+
return ExceptExperimental();
6068
if (_input == "none")
6169
return None();
6270

@@ -74,7 +82,7 @@ std::optional<DebugInfoSelection> DebugInfoSelection::fromComponents(
7482
for (auto const& component: _componentNames)
7583
{
7684
if (component == "*")
77-
return (_acceptWildcards ? std::make_optional(DebugInfoSelection::All()) : std::nullopt);
85+
return (_acceptWildcards ? std::make_optional(ExceptExperimental()) : std::nullopt);
7886

7987
if (!selection.enable(component))
8088
return std::nullopt;

liblangutil/DebugInfoSelection.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ struct DebugInfoSelection
4242
static DebugInfoSelection const All(bool _value = true) noexcept;
4343
static DebugInfoSelection const None() noexcept { return All(false); }
4444
static DebugInfoSelection const Only(bool DebugInfoSelection::* _member) noexcept;
45-
static DebugInfoSelection const Default() noexcept { return All(); }
45+
static DebugInfoSelection const Default() noexcept { return ExceptExperimental(); }
46+
static DebugInfoSelection const Except(std::vector<bool DebugInfoSelection::*> const& _members) noexcept;
47+
static DebugInfoSelection const ExceptExperimental() noexcept { return Except({&DebugInfoSelection::ethdebug}); }
4648

4749
static std::optional<DebugInfoSelection> fromString(std::string_view _input);
4850
static std::optional<DebugInfoSelection> fromComponents(
@@ -72,13 +74,15 @@ struct DebugInfoSelection
7274
{"location", &DebugInfoSelection::location},
7375
{"snippet", &DebugInfoSelection::snippet},
7476
{"ast-id", &DebugInfoSelection::astID},
77+
{"ethdebug", &DebugInfoSelection::ethdebug},
7578
};
7679
return components;
7780
}
7881

7982
bool location = false; ///< Include source location. E.g. `@src 3:50:100`
8083
bool snippet = false; ///< Include source code snippet next to location. E.g. `@src 3:50:100 "contract C {..."`
8184
bool astID = false; ///< Include ID of the Solidity AST node. E.g. `@ast-id 15`
85+
bool ethdebug = false; ///< Include ethdebug related debug information.
8286
};
8387

8488
std::ostream& operator<<(std::ostream& _stream, DebugInfoSelection const& _selection);

libsolidity/codegen/ir/IRGenerator.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ std::string IRGenerator::generate(
120120
);
121121
};
122122

123-
Whiskers t(R"(
123+
Whiskers t(R"(<?isEthdebugEnabled>/// ethdebug: enabled</isEthdebugEnabled>
124124
/// @use-src <useSrcMapCreation>
125125
object "<CreationObject>" {
126126
code {
@@ -157,6 +157,7 @@ std::string IRGenerator::generate(
157157
for (VariableDeclaration const* var: ContractType(_contract).immutableVariables())
158158
m_context.registerImmutableVariable(*var);
159159

160+
t("isEthdebugEnabled", m_context.debugInfoSelection().ethdebug);
160161
t("CreationObject", IRNames::creationObject(_contract));
161162
t("sourceLocationCommentCreation", dispenseLocationComment(_contract));
162163
t("library", _contract.isLibrary());

libsolidity/interface/CompilerStack.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,6 +1166,33 @@ Json CompilerStack::interfaceSymbols(std::string const& _contractName) const
11661166
return interfaceSymbols;
11671167
}
11681168

1169+
Json CompilerStack::ethdebug() const
1170+
{
1171+
Json result = Json::object();
1172+
result["sources"] = sourceNames();
1173+
return result;
1174+
}
1175+
1176+
Json CompilerStack::ethdebug(std::string const& _contractName, bool _runtime) const
1177+
{
1178+
solAssert(m_stackState >= AnalysisSuccessful, "Analysis was not successful.");
1179+
return ethdebug(contract(_contractName), _runtime);
1180+
}
1181+
1182+
Json CompilerStack::ethdebug(Contract const& _contract, bool _runtime) const
1183+
{
1184+
solAssert(m_stackState >= AnalysisSuccessful, "Analysis was not successful.");
1185+
solAssert(_contract.contract);
1186+
solUnimplementedAssert(!isExperimentalSolidity());
1187+
if (_runtime)
1188+
{
1189+
Json result = Json::object();
1190+
return result;
1191+
}
1192+
Json result = Json::object();
1193+
return result;
1194+
}
1195+
11691196
bytes CompilerStack::cborMetadata(std::string const& _contractName, bool _forIR) const
11701197
{
11711198
solAssert(m_stackState >= AnalysisSuccessful, "Analysis was not successful.");

libsolidity/interface/CompilerStack.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,14 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac
391391
/// @returns a JSON object with the three members ``methods``, ``events``, ``errors``. Each is a map, mapping identifiers (hashes) to function names.
392392
Json interfaceSymbols(std::string const& _contractName) const;
393393

394+
/// @returns a JSON representing the ethdebug data of the specified contract.
395+
/// Prerequisite: Successful call to parse or compile.
396+
Json ethdebug(std::string const& _contractName, bool _runtime) const override;
397+
398+
/// @returns a JSON representing the top-level ethdebug data (types, etc.).
399+
/// Prerequisite: Successful call to parse or compile.
400+
Json ethdebug() const override;
401+
394402
/// @returns the Contract Metadata matching the pipeline selected using the viaIR setting.
395403
std::string const& metadata(std::string const& _contractName) const { return metadata(contract(_contractName)); }
396404

@@ -571,6 +579,10 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac
571579
/// This will generate the metadata and store it in the Contract object if it is not present yet.
572580
std::string const& metadata(Contract const& _contract) const;
573581

582+
/// @returns the Contract ethdebug data.
583+
/// This will generate the JSON object and store it in the Contract object if it is not present yet.
584+
Json ethdebug(Contract const& _contract, bool _runtime) const;
585+
574586
/// @returns the offset of the entry point of the given function into the list of assembly items
575587
/// or zero if it is not found or does not exist.
576588
size_t functionEntryPoint(

libsolidity/interface/StandardCompiler.cpp

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,21 +165,28 @@ bool hashMatchesContent(std::string const& _hash, std::string const& _content)
165165

166166
bool isArtifactRequested(Json const& _outputSelection, std::string const& _artifact, bool _wildcardMatchesExperimental)
167167
{
168-
static std::set<std::string> experimental{"ir", "irAst", "irOptimized", "irOptimizedAst", "yulCFGJson"};
168+
static std::set<std::string> experimental{"ir", "irAst", "irOptimized", "irOptimizedAst", "yulCFGJson", "ethdebug"};
169169
for (auto const& selectedArtifactJson: _outputSelection)
170170
{
171171
std::string const& selectedArtifact = selectedArtifactJson.get<std::string>();
172172
if (
173173
_artifact == selectedArtifact ||
174174
boost::algorithm::starts_with(_artifact, selectedArtifact + ".")
175175
)
176+
{
177+
if (_artifact.find("ethdebug") != std::string::npos)
178+
// only accept exact matches for ethdebug, e.g. evm.bytecode.ethdebug
179+
return selectedArtifact == _artifact;
176180
return true;
181+
}
177182
else if (selectedArtifact == "*")
178183
{
179184
// TODO: yulCFGJson is only experimental now, so it should not be matched by "*".
180185
if (_artifact == "yulCFGJson")
181186
return false;
182187
// "ir", "irOptimized" can only be matched by "*" if activated.
188+
if (_artifact.find("ethdebug") != std::string::npos)
189+
return false;
183190
if (experimental.count(_artifact) == 0 || _wildcardMatchesExperimental)
184191
return true;
185192
}
@@ -237,7 +244,7 @@ bool isArtifactRequested(Json const& _outputSelection, std::string const& _file,
237244
std::vector<std::string> evmObjectComponents(std::string const& _objectKind)
238245
{
239246
solAssert(_objectKind == "bytecode" || _objectKind == "deployedBytecode", "");
240-
std::vector<std::string> components{"", ".object", ".opcodes", ".sourceMap", ".functionDebugData", ".generatedSources", ".linkReferences"};
247+
std::vector<std::string> components{"", ".object", ".opcodes", ".sourceMap", ".functionDebugData", ".generatedSources", ".linkReferences", ".ethdebug"};
241248
if (_objectKind == "deployedBytecode")
242249
components.push_back(".immutableReferences");
243250
return util::applyMap(components, [&](auto const& _s) { return "evm." + _objectKind + _s; });
@@ -253,7 +260,7 @@ bool isBinaryRequested(Json const& _outputSelection)
253260
static std::vector<std::string> const outputsThatRequireBinaries = std::vector<std::string>{
254261
"*",
255262
"ir", "irAst", "irOptimized", "irOptimizedAst", "yulCFGJson",
256-
"evm.gasEstimates", "evm.legacyAssembly", "evm.assembly"
263+
"evm.gasEstimates", "evm.legacyAssembly", "evm.assembly", "ethdebug"
257264
} + evmObjectComponents("bytecode") + evmObjectComponents("deployedBytecode");
258265

259266
for (auto const& fileRequests: _outputSelection)
@@ -283,6 +290,21 @@ bool isEvmBytecodeRequested(Json const& _outputSelection)
283290
return false;
284291
}
285292

293+
/// @returns true if ethdebug was requested.
294+
bool isEthdebugRequested(Json const& _outputSelection)
295+
{
296+
if (!_outputSelection.is_object())
297+
return false;
298+
299+
for (auto const& fileRequests: _outputSelection)
300+
for (auto const& requests: fileRequests)
301+
for (auto const& request: requests)
302+
if (request == "evm.bytecode.ethdebug" || request == "evm.deployedBytecode.ethdebug")
303+
return true;
304+
305+
return false;
306+
}
307+
286308
/// @returns The set of selected contracts, along with their compiler pipeline configuration, based
287309
/// on outputs requested in the JSON. Translates wildcards to the ones understood by CompilerStack.
288310
/// Note that as an exception, '*' does not yet match "ir", "irAst", "irOptimized" or "irOptimizedAst".
@@ -378,6 +400,7 @@ Json collectEVMObject(
378400
langutil::EVMVersion _evmVersion,
379401
evmasm::LinkerObject const& _object,
380402
std::string const* _sourceMap,
403+
Json _ethdebug,
381404
Json _generatedSources,
382405
bool _runtimeObject,
383406
std::function<bool(std::string)> const& _artifactRequested
@@ -398,6 +421,9 @@ Json collectEVMObject(
398421
output["immutableReferences"] = formatImmutableReferences(_object.immutableReferences);
399422
if (_artifactRequested("generatedSources"))
400423
output["generatedSources"] = std::move(_generatedSources);
424+
if (_artifactRequested("ethdebug"))
425+
output["ethdebug"] = std::move(_ethdebug);
426+
401427
return output;
402428
}
403429

@@ -1179,6 +1205,35 @@ std::variant<StandardCompiler::InputsAndSettings, Json> StandardCompiler::parseI
11791205
ret.modelCheckerSettings.timeout = modelCheckerSettings["timeout"].get<Json::number_unsigned_t>();
11801206
}
11811207

1208+
if ((ret.debugInfoSelection.has_value() && ret.debugInfoSelection->ethdebug) || isEthdebugRequested(ret.outputSelection))
1209+
{
1210+
if (ret.language != "Solidity" && ret.language != "Yul")
1211+
return formatFatalError(Error::Type::FatalError, "'settings.debug.debugInfo' 'ethdebug' is only supported for languages 'Solidity' and 'Yul'.");
1212+
}
1213+
1214+
if (isEthdebugRequested(ret.outputSelection))
1215+
{
1216+
if (ret.language == "Solidity" && !ret.viaIR)
1217+
return formatFatalError(Error::Type::FatalError, "'evm.bytecode.ethdebug' or 'evm.deployedBytecode.ethdebug' can only be selected as output, if 'viaIR' was set.");
1218+
1219+
if (!ret.debugInfoSelection.has_value())
1220+
{
1221+
ret.debugInfoSelection = DebugInfoSelection::Default();
1222+
ret.debugInfoSelection->enable("ethdebug");
1223+
}
1224+
else
1225+
{
1226+
if (!ret.debugInfoSelection->ethdebug && ret.language == "Solidity")
1227+
return formatFatalError(Error::Type::FatalError, "'ethdebug' needs to be enabled in 'settings.debug.debugInfo', if 'evm.bytecode.ethdebug' or 'evm.deployedBytecode.ethdebug' was selected as output.");
1228+
}
1229+
}
1230+
1231+
if (
1232+
ret.debugInfoSelection.has_value() && ret.debugInfoSelection->ethdebug && ret.language == "Solidity" &&
1233+
!pipelineConfig(ret.outputSelection)[""][""].irCodegen && !isEthdebugRequested(ret.outputSelection)
1234+
)
1235+
return formatFatalError(Error::Type::FatalError, "'settings.debug.debugInfo' can only include 'ethdebug', if output 'ir', 'irOptimized', 'evm.bytecode.ethdebug' or 'evm.deployedBytecode.ethdebug' was selected.");
1236+
11821237
return {std::move(ret)};
11831238
}
11841239

@@ -1254,6 +1309,7 @@ Json StandardCompiler::importEVMAssembly(StandardCompiler::InputsAndSettings _in
12541309
_inputsAndSettings.evmVersion,
12551310
stack.object(sourceName),
12561311
stack.sourceMapping(sourceName),
1312+
stack.ethdebug(sourceName, false),
12571313
{},
12581314
false, // _runtimeObject
12591315
[&](std::string const& _element) {
@@ -1278,6 +1334,7 @@ Json StandardCompiler::importEVMAssembly(StandardCompiler::InputsAndSettings _in
12781334
_inputsAndSettings.evmVersion,
12791335
stack.runtimeObject(sourceName),
12801336
stack.runtimeSourceMapping(sourceName),
1337+
stack.ethdebug(sourceName, true),
12811338
{},
12821339
true, // _runtimeObject
12831340
[&](std::string const& _element) {
@@ -1299,6 +1356,7 @@ Json StandardCompiler::importEVMAssembly(StandardCompiler::InputsAndSettings _in
12991356
contractsOutput[sourceName][""] = contractData;
13001357
Json output;
13011358
output["contracts"] = contractsOutput;
1359+
13021360
return util::removeNullMembers(output);
13031361
}
13041362

@@ -1514,6 +1572,7 @@ Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inpu
15141572
_inputsAndSettings.evmVersion,
15151573
compilerStack.object(contractName),
15161574
compilerStack.sourceMapping(contractName),
1575+
compilerStack.ethdebug(contractName, false),
15171576
compilerStack.generatedSources(contractName),
15181577
false,
15191578
[&](std::string const& _element) { return isArtifactRequested(
@@ -1536,6 +1595,7 @@ Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inpu
15361595
_inputsAndSettings.evmVersion,
15371596
compilerStack.runtimeObject(contractName),
15381597
compilerStack.runtimeSourceMapping(contractName),
1598+
compilerStack.ethdebug(contractName, true),
15391599
compilerStack.generatedSources(contractName, true),
15401600
true,
15411601
[&](std::string const& _element) { return isArtifactRequested(
@@ -1560,6 +1620,9 @@ Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inpu
15601620
if (!contractsOutput.empty())
15611621
output["contracts"] = contractsOutput;
15621622

1623+
if (isEthdebugRequested(_inputsAndSettings.outputSelection))
1624+
output["ethdebug"] = compilerStack.ethdebug();
1625+
15631626
return output;
15641627
}
15651628

@@ -1608,6 +1671,19 @@ Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
16081671
return output;
16091672
}
16101673

1674+
for (auto const& fileRequests: _inputsAndSettings.outputSelection)
1675+
for (auto const& requests: fileRequests)
1676+
for (auto const& request: requests)
1677+
if (request == "evm.deployedBytecode.ethdebug")
1678+
{
1679+
output["errors"].emplace_back(formatError(
1680+
Error::Type::JSONError,
1681+
"general",
1682+
"\"evm.deployedBytecode.ethdebug\" cannot be used for Yul."
1683+
));
1684+
return output;
1685+
}
1686+
16111687
YulStack stack(
16121688
_inputsAndSettings.evmVersion,
16131689
_inputsAndSettings.eofVersion,
@@ -1679,6 +1755,7 @@ Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
16791755
_inputsAndSettings.evmVersion,
16801756
*o.bytecode,
16811757
o.sourceMappings.get(),
1758+
o.ethdebug,
16821759
Json::array(),
16831760
isDeployed,
16841761
[&, kind = kind](std::string const& _element) { return isArtifactRequested(

0 commit comments

Comments
 (0)