Skip to content

Commit

Permalink
Initial ethdebug tool.
Browse files Browse the repository at this point in the history
  • Loading branch information
aarlt committed Jan 30, 2025
1 parent 8ace2ba commit f24eeb9
Show file tree
Hide file tree
Showing 18 changed files with 2,426 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
[submodule "deps/fmtlib"]
path = deps/fmtlib
url = https://github.com/fmtlib/fmt.git
[submodule "deps/imgui"]
path = deps/imgui
url = https://github.com/ocornut/imgui.git
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ option(
"Only build library targets that can be statically linked against. Do not build executables or tests."
OFF
)
option(ETHDEBUG_TOOL "Build the ethdebug tool" OFF)
mark_as_advanced(ETHDEBUG_TOOL)
mark_as_advanced(PROFILE_OPTIMIZER_STEPS)
mark_as_advanced(IGNORE_VENDORED_DEPENDENCIES)
mark_as_advanced(ONLY_BUILD_SOLIDITY_LIBRARIES)
Expand Down
1 change: 1 addition & 0 deletions deps/imgui
Submodule imgui added at 5c1d2d
1 change: 1 addition & 0 deletions scripts/check_style.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ EXCLUDE_FILES=(
"test/scripts/fixtures/smt_contract_with_crlf_newlines.sol"
"test/scripts/fixtures/smt_contract_with_cr_newlines.sol"
"test/scripts/fixtures/smt_contract_with_mixed_newlines.sol"
"tools/ethdebug/src/data/InstructionInfo.h"
)
EXCLUDE_FILES_JOINED=$(printf "%s\|" "${EXCLUDE_FILES[@]}")
EXCLUDE_FILES_JOINED=${EXCLUDE_FILES_JOINED%??}
Expand Down
4 changes: 4 additions & 0 deletions tools/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
include(GNUInstallDirs)

if (ETHDEBUG_TOOL)
add_subdirectory(ethdebug)
endif()

set(libphaser_sources
yulPhaser/Common.h
yulPhaser/Common.cpp
Expand Down
36 changes: 36 additions & 0 deletions tools/ethdebug/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
include(${CMAKE_SOURCE_DIR}/cmake/submodules.cmake)
initialize_submodule(imgui)

find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
find_package(OpenGL REQUIRED)
set(IMGUI_SOURCES
${CMAKE_SOURCE_DIR}/deps/imgui/imgui.cpp
${CMAKE_SOURCE_DIR}/deps/imgui/imgui_draw.cpp
${CMAKE_SOURCE_DIR}/deps/imgui/imgui_tables.cpp
${CMAKE_SOURCE_DIR}/deps/imgui/imgui_widgets.cpp
${CMAKE_SOURCE_DIR}/deps/imgui/backends/imgui_impl_sdl2.cpp
${CMAKE_SOURCE_DIR}/deps/imgui/backends/imgui_impl_opengl3.cpp
)
add_executable(ethdebug-tool
src/main.cpp
src/ui/UserInterface.cpp
src/ui/UserInterface.h
src/ui/Styles.h
src/ui/HighlightedTextView.cpp
src/ui/HighlightedTextView.h
src/ui/InstructionTableView.h
src/ui/InstructionTableView.cpp
src/context/CodeContextDecoder.h
src/Compiler.cpp
src/Compiler.h
src/data/InstructionInfo.h
${IMGUI_SOURCES}
)
target_include_directories(ethdebug-tool PRIVATE
${CMAKE_SOURCE_DIR}/tools/ethdebug/src
${CMAKE_SOURCE_DIR}/deps
${CMAKE_SOURCE_DIR}/deps/imgui
${SDL2_INCLUDE_DIRS}
)
target_link_libraries(ethdebug-tool PRIVATE -L${SDL2_LIBRARY_DIRS} ${SDL2_LIBRARIES} OpenGL::GL solidity Boost::boost Boost::program_options)
117 changes: 117 additions & 0 deletions tools/ethdebug/src/Compiler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#include <Compiler.h>

#include <libsolidity/interface/StandardCompiler.h>
#include <libsolutil/JSON.h>

#include <fstream>

namespace ethdebug
{

Compiler::Compiler(std::set<std::string> const& _sources, bool _optimize): m_files(_sources), m_optimize(_optimize)
{
using namespace solidity::frontend;
solidity::frontend::StandardCompiler compiler;

solidity::Json input;
input["language"] = "Solidity";
(void) m_optimize;

solidity::Json sources;
for (auto const& file: _sources)
{
std::ifstream ifs(file);
if (!ifs.is_open())
{
std::cerr << "Could not open: " << file << "\n";
continue;
}
std::string sourceContent((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
sources[file]["content"] = sourceContent;
m_content[file] = sourceContent;
}
input["sources"] = sources;

solidity::Json settings;
solidity::Json outputSel;
outputSel["*"]["*"] = nlohmann::json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug", "ir"});
settings["outputSelection"] = outputSel;
settings["viaIR"] = true;
if (_optimize)
{
nlohmann::json optimizer;
optimizer["enabled"] = true;
settings["optimizer"] = optimizer;
}
input["settings"] = settings;

m_standardJson = compiler.compile(input);

bool errors = false;
if (m_standardJson.contains("errors"))
for (auto const& error: m_standardJson["errors"])
if (error["severity"] == "error")
{
std::cout << "Error: " << error["formattedMessage"].get<std::string>() << std::endl;
errors = true;
}
if (errors)
exit(1);

parseArtifacts();
}

void Compiler::parseArtifacts()
{
m_ethdebug = m_standardJson["ethdebug"];
for (auto const& [fileName, contracts]: m_standardJson["contracts"].items())
{
for (auto const& [contractName, data]: contracts.items())
{
std::string irContent = m_standardJson["contracts"][fileName][contractName]["ir"].get<std::string>();
if (!irContent.empty())
{
Artifacts::Ptr artifact = std::make_shared<Artifacts>();
artifact->name = contractName;
artifact->ethdebugCreation = data["evm"]["bytecode"]["ethdebug"];
artifact->ethdebugRuntime = data["evm"]["deployedBytecode"]["ethdebug"];
artifact->yulCode = irContent;
artifact->solidityCode = m_content[fileName];
m_contracts[fileName][contractName] = artifact;
}
}
}
}

Artifacts::Ptr Compiler::artifact(std::string const& _filename, std::string const& _contractName)
{
if (m_contracts.count(_filename) && m_contracts[_filename].count(_contractName))
return m_contracts[_filename][_contractName];
return {};
}

solidity::Json const&
Compiler::instructions(std::string const& _filename, std::string const& _contractName, bool _creationCode)
{
if (m_contracts.count(_filename) && m_contracts[_filename][_contractName])
return _creationCode
? m_contracts[_filename][_contractName]->ethdebugCreation["instructions"]
: m_contracts[_filename][_contractName]->ethdebugRuntime["instructions"];

static solidity::Json empty = solidity::Json::array();
return empty;
}

solidity::Json const& Compiler::instruction(std::string const& _filename, std::string const& _contractName, bool _creationCode, size_t _instructionIndex)
{
solidity::Json const& instructions = this->instructions(_filename, _contractName, _creationCode);
solAssert(instructions.is_array());
if (_instructionIndex >= 0 && _instructionIndex < instructions.size())
{
return instructions[_instructionIndex];
}
static solidity::Json empty = solidity::Json::object();
return empty;
}

} // ethdebug
58 changes: 58 additions & 0 deletions tools/ethdebug/src/Compiler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#pragma once

#include <libsolutil/JSON.h>
#include <range/v3/range.hpp>
#include <range/v3/view/map.hpp>
#include <set>
#include <string>

namespace ethdebug
{

struct Artifacts
{
typedef std::shared_ptr<Artifacts> Ptr;

std::string name;
std::string solidityCode;
std::string yulCode;
solidity::Json ethdebugCreation;
solidity::Json ethdebugRuntime;
};

class Compiler
{
public:
typedef std::shared_ptr<Compiler> Ptr;
Compiler(std::set<std::string> const& _sources, bool _optimize);

std::vector<std::string> files()
{
return m_contracts | ranges::views::keys | ranges::to<std::vector<std::string>>();
}

std::vector<std::string> contracts(std::string const& _filename)
{
return m_contracts[_filename] | ranges::views::keys | ranges::to<std::vector<std::string>>();
}

Artifacts::Ptr artifact(std::string const& _filename, std::string const& _contractName);

solidity::Json const& instructions(std::string const& _filename, std::string const& _contractName, bool _creationCode);

solidity::Json const& instruction(std::string const& _filename, std::string const& _contractName, bool _creationCode, size_t _instructionIndex);

solidity::Json const& ethdebug() const { return m_ethdebug; }

private:
void parseArtifacts();

std::set<std::string> m_files;
bool m_optimize;
std::map<std::string, std::string> m_content;
solidity::Json m_ethdebug;
std::map<std::string, std::map<std::string, Artifacts::Ptr>> m_contracts;
solidity::Json m_standardJson;
};

} // ethdebug
18 changes: 18 additions & 0 deletions tools/ethdebug/src/context/CodeContextDecoder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <fmt/printf.h>
#include <libsolutil/JSON.h>

namespace ethdebug
{

inline std::string CodeContextDecoder(solidity::Json const& _ethdebug, solidity::Json const&, solidity::Json const& _context)
{
if (_ethdebug.is_object() && _ethdebug.contains("sources") && _ethdebug["sources"].is_array() && _context.is_object() && _context.contains("source") && _context["source"].contains("id"))
if (_context["source"]["id"].get<int>() >= 0 && _context["source"]["id"].get<uint64_t>() < _ethdebug["sources"].size())
return fmt::sprintf("Source: %s", (_ethdebug["sources"][(_context["source"]["id"].get<uint64_t>())]).get<std::string>());
return {};
}


} // namespace ethdebug
Loading

0 comments on commit f24eeb9

Please sign in to comment.