Skip to content

Commit

Permalink
[solc] Enable ethdebug debug info and output selection.
Browse files Browse the repository at this point in the history
  • Loading branch information
aarlt committed Aug 1, 2024
1 parent 1bb7872 commit 9772d20
Show file tree
Hide file tree
Showing 44 changed files with 560 additions and 22 deletions.
12 changes: 10 additions & 2 deletions liblangutil/DebugInfoSelection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,22 @@ DebugInfoSelection const DebugInfoSelection::Only(bool DebugInfoSelection::* _me
return result;
}

DebugInfoSelection const DebugInfoSelection::Except(std::vector<bool DebugInfoSelection::*> const& _members) noexcept
{
DebugInfoSelection result = All();
for (bool DebugInfoSelection::* member: _members)
result.*member = false;
return result;
}

std::optional<DebugInfoSelection> DebugInfoSelection::fromString(std::string_view _input)
{
// TODO: Make more stuff constexpr and make it a static_assert().
solAssert(componentMap().count("all") == 0, "");
solAssert(componentMap().count("none") == 0, "");

if (_input == "all")
return All();
return ExceptExperimental();
if (_input == "none")
return None();

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

if (!selection.enable(component))
return std::nullopt;
Expand Down
6 changes: 5 additions & 1 deletion liblangutil/DebugInfoSelection.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ struct DebugInfoSelection
static DebugInfoSelection const All(bool _value = true) noexcept;
static DebugInfoSelection const None() noexcept { return All(false); }
static DebugInfoSelection const Only(bool DebugInfoSelection::* _member) noexcept;
static DebugInfoSelection const Default() noexcept { return All(); }
static DebugInfoSelection const Default() noexcept { return ExceptExperimental(); }
static DebugInfoSelection const Except(std::vector<bool DebugInfoSelection::*> const& _members) noexcept;
static DebugInfoSelection const ExceptExperimental() noexcept { return Except({&DebugInfoSelection::ethdebug}); }

static std::optional<DebugInfoSelection> fromString(std::string_view _input);
static std::optional<DebugInfoSelection> fromComponents(
Expand Down Expand Up @@ -72,13 +74,15 @@ struct DebugInfoSelection
{"location", &DebugInfoSelection::location},
{"snippet", &DebugInfoSelection::snippet},
{"ast-id", &DebugInfoSelection::astID},
{"ethdebug", &DebugInfoSelection::ethdebug},
};
return components;
}

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

std::ostream& operator<<(std::ostream& _stream, DebugInfoSelection const& _selection);
Expand Down
3 changes: 2 additions & 1 deletion libsolidity/codegen/ir/IRGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ std::string IRGenerator::generate(
);
};

Whiskers t(R"(
Whiskers t(R"(<?isEthdebugEnabled>/// ethdebug: enabled</isEthdebugEnabled>
/// @use-src <useSrcMapCreation>
object "<CreationObject>" {
code {
Expand Down Expand Up @@ -155,6 +155,7 @@ std::string IRGenerator::generate(
for (VariableDeclaration const* var: ContractType(_contract).immutableVariables())
m_context.registerImmutableVariable(*var);

t("isEthdebugEnabled", m_context.debugInfoSelection().ethdebug);
t("CreationObject", IRNames::creationObject(_contract));
t("sourceLocationCommentCreation", dispenseLocationComment(_contract));
t("library", _contract.isLibrary());
Expand Down
8 changes: 8 additions & 0 deletions libsolidity/interface/CompilerStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,14 @@ Json CompilerStack::interfaceSymbols(std::string const& _contractName) const
return interfaceSymbols;
}

Json CompilerStack::ethdebug(std::string const& _contractName) const
{
(void)_contractName;

Json ethdebug = Json::object();
return ethdebug;
}

bytes CompilerStack::cborMetadata(std::string const& _contractName, bool _forIR) const
{
solAssert(m_stackState >= AnalysisSuccessful, "Analysis was not successful.");
Expand Down
4 changes: 4 additions & 0 deletions libsolidity/interface/CompilerStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,10 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac
/// @returns a JSON object with the three members ``methods``, ``events``, ``errors``. Each is a map, mapping identifiers (hashes) to function names.
Json interfaceSymbols(std::string const& _contractName) const;

/// @returns a JSON representing the ethdebug data of the specified contract.
/// Prerequisite: Successful call to parse or compile.
Json ethdebug(std::string const& _contractName) const;

/// @returns the Contract Metadata matching the pipeline selected using the viaIR setting.
std::string const& metadata(std::string const& _contractName) const { return metadata(contract(_contractName)); }

Expand Down
10 changes: 9 additions & 1 deletion libsolidity/interface/StandardCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1321,10 +1321,18 @@ Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inpu
compilerStack.setModelCheckerSettings(_inputsAndSettings.modelCheckerSettings);

compilerStack.enableEvmBytecodeGeneration(isEvmBytecodeRequested(_inputsAndSettings.outputSelection));
compilerStack.requestIROutputs(irOutputSelection(_inputsAndSettings.outputSelection));
CompilerStack::IROutputSelection selectedIrOutput = irOutputSelection(_inputsAndSettings.outputSelection);
compilerStack.requestIROutputs(selectedIrOutput);

Json errors = std::move(_inputsAndSettings.errors);

if (
_inputsAndSettings.debugInfoSelection.has_value() &&
_inputsAndSettings.debugInfoSelection->ethdebug &&
(selectedIrOutput == CompilerStack::IROutputSelection::None && !_inputsAndSettings.viaIR)
)
errors.emplace_back(formatError(Error::Type::FatalError, "general", "'ethdebug' can only be selected in 'settings.debug.debugInfo' when at least one of the IR outputs is selected or 'viaIR' was set."));

bool const binariesRequested = isBinaryRequested(_inputsAndSettings.outputSelection);

try
Expand Down
13 changes: 7 additions & 6 deletions libyul/YulStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,12 +360,13 @@ std::string YulStack::print(
yulAssert(m_stackState >= Parsed);
yulAssert(m_parserResult, "");
yulAssert(m_parserResult->code, "");
return m_parserResult->toString(
languageToDialect(m_language, m_evmVersion),
AsmPrinter::TypePrinting::OmitDefault,
m_debugInfoSelection,
_soliditySourceProvider
) + "\n";
return (m_debugInfoSelection.ethdebug ? "/// ethdebug: enabled\n" : "") +
m_parserResult->toString(
languageToDialect(m_language, m_evmVersion),
AsmPrinter::TypePrinting::OmitDefault,
m_debugInfoSelection,
_soliditySourceProvider
) + "\n";
}

Json YulStack::astJson() const
Expand Down
18 changes: 17 additions & 1 deletion solc/CommandLineInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ static bool needsHumanTargetedStdout(CommandLineOptions const& _options)
_options.compiler.outputs.natspecDev ||
_options.compiler.outputs.opcodes ||
_options.compiler.outputs.signatureHashes ||
_options.compiler.outputs.storageLayout;
_options.compiler.outputs.storageLayout ||
_options.compiler.outputs.ethdebug;
}

static bool coloredOutput(CommandLineOptions const& _options)
Expand Down Expand Up @@ -505,6 +506,20 @@ void CommandLineInterface::handleGasEstimation(std::string const& _contract)
}
}

void CommandLineInterface::handleEthdebug(std::string const& _contract)
{
solAssert(CompilerInputModes.count(m_options.input.mode) == 1);

if (!m_options.compiler.outputs.ethdebug)
return;

std::string data = jsonPrint(removeNullMembers(m_compiler->ethdebug(_contract)), m_options.formatting.json);
if (!m_options.output.dir.empty())
createFile(m_compiler->filesystemFriendlyName(_contract) + "_ethdebug.json", data);
else
sout() << "Debug Data (ethdebug):" << std::endl << data << std::endl;
}

void CommandLineInterface::readInputFiles()
{
solAssert(!m_standardJsonInput.has_value());
Expand Down Expand Up @@ -1324,6 +1339,7 @@ void CommandLineInterface::outputCompilationResults()
handleStorageLayout(contract);
handleNatspec(true, contract);
handleNatspec(false, contract);
handleEthdebug(contract);
} // end of contracts iteration
}

Expand Down
1 change: 1 addition & 0 deletions solc/CommandLineInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ class CommandLineInterface
void handleNatspec(bool _natspecDev, std::string const& _contract);
void handleGasEstimation(std::string const& _contract);
void handleStorageLayout(std::string const& _contract);
void handleEthdebug(std::string const& _contract);

/// Tries to read @ m_sourceCodes as a JSONs holding ASTs
/// such that they can be imported into the compiler (importASTs())
Expand Down
39 changes: 38 additions & 1 deletion solc/CommandLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ void CommandLineParser::parseOutputSelection()
CompilerOutputs::componentName(&CompilerOutputs::irOptimized),
CompilerOutputs::componentName(&CompilerOutputs::astCompactJson),
CompilerOutputs::componentName(&CompilerOutputs::asmJson),
CompilerOutputs::componentName(&CompilerOutputs::ethdebug),
};
static std::set<std::string> const evmAssemblyJsonImportModeOutputs = {
CompilerOutputs::componentName(&CompilerOutputs::asm_),
Expand Down Expand Up @@ -638,7 +639,13 @@ General Information)").c_str(),
po::value<std::string>()->default_value(util::toString(DebugInfoSelection::Default())),
("Debug info components to be included in the produced EVM assembly and Yul code. "
"Value can be all, none or a comma-separated list containing one or more of the "
"following components: " + util::joinHumanReadable(DebugInfoSelection::componentMap() | ranges::views::keys) + ".").c_str()
"following components: " +
util::joinHumanReadable(
DebugInfoSelection::componentMap() | ranges::views::keys |
// Note: We intentionally keep ethdebug undocumented for now.
ranges::views::filter([](std::string const& key) { return key != "ethdebug"; }) |
ranges::to<std::vector>()
) + ".").c_str()
)
(
g_strStopAfter.c_str(),
Expand Down Expand Up @@ -762,6 +769,13 @@ General Information)").c_str(),
(CompilerOutputs::componentName(&CompilerOutputs::metadata).c_str(), "Combined Metadata JSON whose IPFS hash is stored on-chain.")
(CompilerOutputs::componentName(&CompilerOutputs::storageLayout).c_str(), "Slots, offsets and types of the contract's state variables.")
;
if (!_forHelp) // Note: We intentionally keep this undocumented for now.
outputComponents.add_options()
(
CompilerOutputs::componentName(&CompilerOutputs::ethdebug).c_str(),
"Ethdebug output of all contracts."
)
;
desc.add(outputComponents);

po::options_description extraOutput("Extra Output");
Expand Down Expand Up @@ -1440,6 +1454,29 @@ void CommandLineParser::processArgs()
m_options.input.mode == InputMode::CompilerWithASTImport ||
m_options.input.mode == InputMode::EVMAssemblerJSON
);

if (m_options.compiler.outputs.ethdebug)
{
m_options.output.viaIR = true;
if (m_options.output.debugInfoSelection.has_value())
m_options.output.debugInfoSelection->ethdebug = true;
else
{
m_options.output.debugInfoSelection = DebugInfoSelection::Default();
m_options.output.debugInfoSelection->enable("ethdebug");
}
}

if (
m_options.output.debugInfoSelection.has_value() && m_options.output.debugInfoSelection->ethdebug &&
!(m_options.compiler.outputs.ir || m_options.compiler.outputs.irOptimized || m_options.compiler.outputs.ethdebug)
)
solThrow(
CommandLineValidationError,
"--debug-info ethdebug can only be used with --" + CompilerOutputs::componentName(&CompilerOutputs::ir) +
", --" + CompilerOutputs::componentName(&CompilerOutputs::irOptimized) +
" and/or --" + CompilerOutputs::componentName(&CompilerOutputs::ethdebug) + "."
);
}

void CommandLineParser::parseCombinedJsonOption()
Expand Down
2 changes: 2 additions & 0 deletions solc/CommandLineParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ struct CompilerOutputs
{"devdoc", &CompilerOutputs::natspecDev},
{"metadata", &CompilerOutputs::metadata},
{"storage-layout", &CompilerOutputs::storageLayout},
{"ethdebug", &CompilerOutputs::ethdebug},
};
return components;
}
Expand All @@ -106,6 +107,7 @@ struct CompilerOutputs
bool natspecDev = false;
bool metadata = false;
bool storageLayout = false;
bool ethdebug = false;
};

struct CombinedJsonRequests
Expand Down
1 change: 1 addition & 0 deletions test/cmdlineTests/debug_info_ethdebug_no_ir/args
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--debug-info ethdebug
1 change: 1 addition & 0 deletions test/cmdlineTests/debug_info_ethdebug_no_ir/err
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Error: --debug-info ethdebug can only be used with --ir, --ir-optimized and/or --ethdebug.
1 change: 1 addition & 0 deletions test/cmdlineTests/debug_info_ethdebug_no_ir/exit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
6 changes: 6 additions & 0 deletions test/cmdlineTests/debug_info_ethdebug_no_ir/input.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
pragma solidity >=0.0;

contract C {
function f() public {}
}
1 change: 1 addition & 0 deletions test/cmdlineTests/debug_info_in_yul_ethdebug/args
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--ir --debug-info ethdebug
6 changes: 6 additions & 0 deletions test/cmdlineTests/debug_info_in_yul_ethdebug/input.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
pragma solidity >=0.0;

contract C {
function f() public {}
}
Loading

0 comments on commit 9772d20

Please sign in to comment.